1 ;;; mml2015.el --- MIME Security with Pretty Good Privacy (PGP)
3 ;; Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
4 ;; 2008, 2009, 2010 Free Software Foundation, Inc.
6 ;; Author: Shenghuo Zhu <zsh@cs.rochester.edu>
7 ;; Keywords: PGP MIME MML
9 ;; 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.
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
26 ;; RFC 2015 is updated by RFC 3156, this file should be compatible
33 (unless (fboundp 'declare-function) (defmacro declare-function (&rest r)))
35 (if (locate-library "password-cache")
36 (require 'password-cache)
39 (eval-when-compile (require 'cl))
45 (defvar mc-pgp-always-sign)
47 (declare-function epg-check-configuration "ext:epg-config"
48 (config &optional minimum-version))
49 (declare-function epg-configuration "ext:epg-config" ())
51 (defvar mml2015-use (or
55 (epg-check-configuration (epg-configuration))
60 ;; Avoid the "Recursive load suspected" error
62 (let ((recursive-load-depth-limit 100))
64 (and (fboundp 'pgg-sign-region)
69 (and (fboundp 'gpg-sign-detached)
73 (and (fboundp 'mc-encrypt-generic)
74 (fboundp 'mc-sign-generic)
75 (fboundp 'mc-cleanup-recipient-headers)
77 "The package used for PGP/MIME.
78 Valid packages include `epg', `pgg', `gpg' and `mailcrypt'.")
80 ;; Something is not RFC2015.
81 (defvar mml2015-function-alist
82 '((mailcrypt mml2015-mailcrypt-sign
83 mml2015-mailcrypt-encrypt
84 mml2015-mailcrypt-verify
85 mml2015-mailcrypt-decrypt
86 mml2015-mailcrypt-clear-verify
87 mml2015-mailcrypt-clear-decrypt)
92 mml2015-gpg-clear-verify
93 mml2015-gpg-clear-decrypt)
98 mml2015-pgg-clear-verify
99 mml2015-pgg-clear-decrypt)
100 (epg mml2015-epg-sign
104 mml2015-epg-clear-verify
105 mml2015-epg-clear-decrypt))
106 "Alist of PGP/MIME functions.")
108 (defvar mml2015-result-buffer nil)
110 (defcustom mml2015-unabbrev-trust-alist
111 '(("TRUST_UNDEFINED" . nil)
112 ("TRUST_NEVER" . nil)
113 ("TRUST_MARGINAL" . t)
115 ("TRUST_ULTIMATE" . t))
116 "Map GnuPG trust output values to a boolean saying if you trust the key."
118 :group 'mime-security
119 :type '(repeat (cons (regexp :tag "GnuPG output regexp")
120 (boolean :tag "Trust key"))))
122 (defcustom mml2015-verbose mml-secure-verbose
123 "If non-nil, ask the user about the current operation more verbosely."
124 :group 'mime-security
127 (defcustom mml2015-cache-passphrase mml-secure-cache-passphrase
128 "If t, cache passphrase."
129 :group 'mime-security
132 (defcustom mml2015-passphrase-cache-expiry mml-secure-passphrase-cache-expiry
133 "How many seconds the passphrase is cached.
134 Whether the passphrase is cached at all is controlled by
135 `mml2015-cache-passphrase'."
136 :group 'mime-security
139 (defcustom mml2015-signers nil
140 "A list of your own key ID which will be used to sign a message."
141 :group 'mime-security
142 :type '(repeat (string :tag "Key ID")))
144 (defcustom mml2015-encrypt-to-self nil
145 "If t, add your own key ID to recipient list when encryption."
146 :group 'mime-security
149 (defcustom mml2015-always-trust t
150 "If t, GnuPG skip key validation on encryption."
151 :group 'mime-security
154 ;; Extract plaintext from cleartext signature. IMO, this kind of task
155 ;; should be done by GnuPG rather than Elisp, but older PGP backends
156 ;; (such as Mailcrypt, PGG, and gpg.el) discard the output from GnuPG.
157 (defun mml2015-extract-cleartext-signature ()
159 ;; <54a15d860801080142l70b95d7dkac4bf51a86196011@mail.gmail.com>: ``I still
160 ;; believe that the right way is to use the plaintext output from GnuPG as
161 ;; it is, and mml2015-extract-cleartext-signature is just a kludge for
162 ;; misdesigned libraries like PGG, which have no ability to do that. So, I
163 ;; think it should not have descriptive documentation.''
165 ;; This function doesn't handle NotDashEscaped correctly. EasyPG handles it
167 ;; http://thread.gmane.org/gmane.emacs.gnus.general/66062/focus=66082
168 ;; http://thread.gmane.org/gmane.emacs.gnus.general/65087/focus=65109
169 (goto-char (point-min))
171 ;; We need to be careful not to strip beyond the armor headers.
172 ;; Previously, an attacker could replace the text inside our
173 ;; markup with trailing garbage by injecting whitespace into the
175 (while (looking-at "Hash:") ; The only header allowed in cleartext
176 (forward-line)) ; signatures according to RFC2440.
177 (when (looking-at "[\t ]*$")
179 (delete-region (point-min) (point))
180 (if (re-search-forward "^-----BEGIN PGP SIGNATURE-----" nil t)
181 (delete-region (match-beginning 0) (point-max)))
182 (goto-char (point-min))
183 (while (re-search-forward "^- " nil t)
184 (replace-match "" t t)
187 ;;; mailcrypt wrapper
189 (autoload 'mailcrypt-decrypt "mailcrypt")
190 (autoload 'mailcrypt-verify "mailcrypt")
191 (autoload 'mc-pgp-always-sign "mailcrypt")
192 (autoload 'mc-encrypt-generic "mc-toplev")
193 (autoload 'mc-cleanup-recipient-headers "mc-toplev")
194 (autoload 'mc-sign-generic "mc-toplev")
196 (defvar mc-default-scheme)
199 (defvar mml2015-decrypt-function 'mailcrypt-decrypt)
200 (defvar mml2015-verify-function 'mailcrypt-verify)
202 (defun mml2015-format-error (err)
203 (if (stringp (cadr err))
205 (format "%S" (cdr err))))
207 (defun mml2015-mailcrypt-decrypt (handle ctl)
209 (let (child handles result)
210 (unless (setq child (mm-find-part-by-type
212 "application/octet-stream" nil t))
213 (mm-set-handle-multipart-parameter
214 mm-security-handle 'gnus-info "Corrupted")
215 (throw 'error handle))
217 (mm-insert-part child)
220 (funcall mml2015-decrypt-function)
222 (mm-set-handle-multipart-parameter
223 mm-security-handle 'gnus-details (mml2015-format-error err))
226 (mm-set-handle-multipart-parameter
227 mm-security-handle 'gnus-details "Quit.")
230 (mm-set-handle-multipart-parameter
231 mm-security-handle 'gnus-info "Failed")
232 (throw 'error handle))
233 (setq handles (mm-dissect-buffer t)))
234 (mm-destroy-parts handle)
235 (mm-set-handle-multipart-parameter
236 mm-security-handle 'gnus-info
238 (let ((sig (with-current-buffer mml2015-result-buffer
239 (mml2015-gpg-extract-signature-details))))
240 (concat ", Signer: " sig))))
241 (if (listp (car handles))
245 (defun mml2015-mailcrypt-clear-decrypt ()
249 (funcall mml2015-decrypt-function)
251 (mm-set-handle-multipart-parameter
252 mm-security-handle 'gnus-details (mml2015-format-error err))
255 (mm-set-handle-multipart-parameter
256 mm-security-handle 'gnus-details "Quit.")
259 (mm-set-handle-multipart-parameter
260 mm-security-handle 'gnus-info "OK")
261 (mm-set-handle-multipart-parameter
262 mm-security-handle 'gnus-info "Failed"))))
264 (defun mml2015-fix-micalg (alg)
266 ;; Mutt/1.2.5i has seen sending micalg=php-sha1
267 (upcase (if (string-match "^p[gh]p-" alg)
268 (substring alg (match-end 0))
271 (defun mml2015-mailcrypt-verify (handle ctl)
274 (unless (setq part (mm-find-raw-part-by-type
275 ctl (or (mm-handle-multipart-ctl-parameter
277 "application/pgp-signature")
279 (mm-set-handle-multipart-parameter
280 mm-security-handle 'gnus-info "Corrupted")
281 (throw 'error handle))
283 (insert "-----BEGIN PGP SIGNED MESSAGE-----\n")
284 (insert (format "Hash: %s\n\n"
285 (or (mml2015-fix-micalg
286 (mm-handle-multipart-ctl-parameter
290 (narrow-to-region (point) (point))
292 (goto-char (point-min))
294 (if (looking-at "^-")
297 (unless (setq part (mm-find-part-by-type
298 (cdr handle) "application/pgp-signature" nil t))
299 (mm-set-handle-multipart-parameter
300 mm-security-handle 'gnus-info "Corrupted")
301 (throw 'error handle))
303 (narrow-to-region (point) (point))
304 (mm-insert-part part)
305 (goto-char (point-min))
306 (if (re-search-forward "^-----BEGIN PGP [^-]+-----\r?$" nil t)
307 (replace-match "-----BEGIN PGP SIGNATURE-----" t t))
308 (if (re-search-forward "^-----END PGP [^-]+-----\r?$" nil t)
309 (replace-match "-----END PGP SIGNATURE-----" t t)))
310 (let ((mc-gpg-debug-buffer (get-buffer-create " *gnus gpg debug*")))
311 (unless (condition-case err
313 (funcall mml2015-verify-function)
314 (if (get-buffer " *mailcrypt stderr temp")
315 (mm-set-handle-multipart-parameter
316 mm-security-handle 'gnus-details
317 (with-current-buffer " *mailcrypt stderr temp"
319 (if (get-buffer " *mailcrypt stdout temp")
320 (kill-buffer " *mailcrypt stdout temp"))
321 (if (get-buffer " *mailcrypt stderr temp")
322 (kill-buffer " *mailcrypt stderr temp"))
323 (if (get-buffer " *mailcrypt status temp")
324 (kill-buffer " *mailcrypt status temp"))
325 (if (get-buffer mc-gpg-debug-buffer)
326 (kill-buffer mc-gpg-debug-buffer)))
328 (mm-set-handle-multipart-parameter
329 mm-security-handle 'gnus-details (mml2015-format-error err))
332 (mm-set-handle-multipart-parameter
333 mm-security-handle 'gnus-details "Quit.")
335 (mm-set-handle-multipart-parameter
336 mm-security-handle 'gnus-info "Failed")
337 (throw 'error handle))))
338 (mm-set-handle-multipart-parameter
339 mm-security-handle 'gnus-info "OK")
342 (defun mml2015-mailcrypt-clear-verify ()
343 (let ((mc-gpg-debug-buffer (get-buffer-create " *gnus gpg debug*")))
344 (if (condition-case err
346 (funcall mml2015-verify-function)
347 (if (get-buffer " *mailcrypt stderr temp")
348 (mm-set-handle-multipart-parameter
349 mm-security-handle 'gnus-details
350 (with-current-buffer " *mailcrypt stderr temp"
352 (if (get-buffer " *mailcrypt stdout temp")
353 (kill-buffer " *mailcrypt stdout temp"))
354 (if (get-buffer " *mailcrypt stderr temp")
355 (kill-buffer " *mailcrypt stderr temp"))
356 (if (get-buffer " *mailcrypt status temp")
357 (kill-buffer " *mailcrypt status temp"))
358 (if (get-buffer mc-gpg-debug-buffer)
359 (kill-buffer mc-gpg-debug-buffer)))
361 (mm-set-handle-multipart-parameter
362 mm-security-handle 'gnus-details (mml2015-format-error err))
365 (mm-set-handle-multipart-parameter
366 mm-security-handle 'gnus-details "Quit.")
368 (mm-set-handle-multipart-parameter
369 mm-security-handle 'gnus-info "OK")
370 (mm-set-handle-multipart-parameter
371 mm-security-handle 'gnus-info "Failed")))
372 (mml2015-extract-cleartext-signature))
374 (defun mml2015-mailcrypt-sign (cont)
375 (mc-sign-generic (message-options-get 'message-sender)
377 (let ((boundary (mml-compute-boundary cont))
379 (goto-char (point-min))
380 (unless (re-search-forward "^-----BEGIN PGP SIGNED MESSAGE-----\r?$" nil t)
381 (error "Cannot find signed begin line"))
382 (goto-char (match-beginning 0))
384 (unless (looking-at "Hash:[ \t]*\\([a-zA-Z0-9]+\\)")
385 (error "Cannot not find PGP hash"))
386 (setq hash (match-string 1))
387 (unless (re-search-forward "^$" nil t)
388 (error "Cannot not find PGP message"))
390 (delete-region (point-min) (point))
391 (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"