]> code.delx.au - gnu-emacs/blob - lisp/gnus/mml2015.el
; Merge from origin/emacs-25
[gnu-emacs] / lisp / gnus / mml2015.el
1 ;;; mml2015.el --- MIME Security with Pretty Good Privacy (PGP)
2
3 ;; Copyright (C) 2000-2016 Free Software Foundation, Inc.
4
5 ;; Author: Shenghuo Zhu <zsh@cs.rochester.edu>
6 ;; Keywords: PGP MIME MML
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
14
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
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
22
23 ;;; Commentary:
24
25 ;; RFC 2015 is updated by RFC 3156, this file should be compatible
26 ;; with both.
27
28 ;;; Code:
29
30 (eval-and-compile
31 (if (locate-library "password-cache")
32 (require 'password-cache)
33 (require 'password)))
34
35 (eval-when-compile (require 'cl))
36 (require 'mm-decode)
37 (require 'mm-util)
38 (require 'mml)
39 (require 'mml-sec)
40
41 (defvar mc-pgp-always-sign)
42
43 (declare-function epg-check-configuration "ext:epg-config"
44 (config &optional minimum-version))
45 (declare-function epg-configuration "ext:epg-config" ())
46
47 ;; Maybe this should be in eg mml-sec.el (and have a different name).
48 ;; Then mml1991 would not need to require mml2015, and mml1991-use
49 ;; could be removed.
50 (defvar mml2015-use (or
51 (progn
52 (ignore-errors (require 'epg-config))
53 (and (fboundp 'epg-check-configuration)
54 'epg))
55 (progn
56 (let ((abs-file (locate-library "pgg")))
57 ;; Don't load PGG if it is marked as obsolete
58 ;; (Emacs 24).
59 (when (and abs-file
60 (not (string-match "/obsolete/[^/]*\\'"
61 abs-file)))
62 (ignore-errors (require 'pgg))
63 (and (fboundp 'pgg-sign-region)
64 'pgg))))
65 (progn (ignore-errors
66 (load "mc-toplev"))
67 (and (fboundp 'mc-encrypt-generic)
68 (fboundp 'mc-sign-generic)
69 (fboundp 'mc-cleanup-recipient-headers)
70 'mailcrypt)))
71 "The package used for PGP/MIME.
72 Valid packages include `epg', `pgg' and `mailcrypt'.")
73
74 ;; Something is not RFC2015.
75 (defvar mml2015-function-alist
76 '((mailcrypt mml2015-mailcrypt-sign
77 mml2015-mailcrypt-encrypt
78 mml2015-mailcrypt-verify
79 mml2015-mailcrypt-decrypt
80 mml2015-mailcrypt-clear-verify
81 mml2015-mailcrypt-clear-decrypt)
82 (pgg mml2015-pgg-sign
83 mml2015-pgg-encrypt
84 mml2015-pgg-verify
85 mml2015-pgg-decrypt
86 mml2015-pgg-clear-verify
87 mml2015-pgg-clear-decrypt)
88 (epg mml2015-epg-sign
89 mml2015-epg-encrypt
90 mml2015-epg-verify
91 mml2015-epg-decrypt
92 mml2015-epg-clear-verify
93 mml2015-epg-clear-decrypt))
94 "Alist of PGP/MIME functions.")
95
96 (defvar mml2015-result-buffer nil)
97
98 (defcustom mml2015-unabbrev-trust-alist
99 '(("TRUST_UNDEFINED" . nil)
100 ("TRUST_NEVER" . nil)
101 ("TRUST_MARGINAL" . t)
102 ("TRUST_FULLY" . t)
103 ("TRUST_ULTIMATE" . t))
104 "Map GnuPG trust output values to a boolean saying if you trust the key."
105 :version "22.1"
106 :group 'mime-security
107 :type '(repeat (cons (regexp :tag "GnuPG output regexp")
108 (boolean :tag "Trust key"))))
109
110 (defcustom mml2015-cache-passphrase mml-secure-cache-passphrase
111 "If t, cache passphrase."
112 :group 'mime-security
113 :type 'boolean)
114 (make-obsolete-variable 'mml2015-cache-passphrase
115 'mml-secure-cache-passphrase
116 "25.1")
117
118 (defcustom mml2015-passphrase-cache-expiry mml-secure-passphrase-cache-expiry
119 "How many seconds the passphrase is cached.
120 Whether the passphrase is cached at all is controlled by
121 `mml2015-cache-passphrase'."
122 :group 'mime-security
123 :type 'integer)
124 (make-obsolete-variable 'mml2015-passphrase-cache-expiry
125 'mml-secure-passphrase-cache-expiry
126 "25.1")
127
128 (defcustom mml2015-signers nil
129 "A list of your own key ID(s) which will be used to sign a message.
130 If set, it overrides the setting of `mml2015-sign-with-sender'."
131 :group 'mime-security
132 :type '(repeat (string :tag "Key ID")))
133
134 (defcustom mml2015-sign-with-sender nil
135 "If t, use message sender so find a key to sign with."
136 :group 'mime-security
137 :type 'boolean
138 :version "24.1")
139
140 (defcustom mml2015-encrypt-to-self nil
141 "If t, add your own key ID to recipient list when encryption."
142 :group 'mime-security
143 :type 'boolean)
144
145 (defcustom mml2015-always-trust t
146 "If t, GnuPG skip key validation on encryption."
147 :group 'mime-security
148 :type 'boolean)
149
150 (defcustom mml2015-maximum-key-image-dimension 64
151 "The maximum dimension (width or height) of any key images."
152 :version "24.4"
153 :group 'mime-security
154 :type 'integer)
155
156 (defcustom mml2015-display-key-image t
157 "If t, try to display key images."
158 :version "24.5"
159 :group 'mime-security
160 :type 'boolean)
161
162 ;; Extract plaintext from cleartext signature. IMO, this kind of task
163 ;; should be done by GnuPG rather than Elisp, but older PGP backends
164 ;; (such as Mailcrypt, and PGG) discard the output from GnuPG.
165 (defun mml2015-extract-cleartext-signature ()
166 ;; Daiki Ueno in
167 ;; <54a15d860801080142l70b95d7dkac4bf51a86196011@mail.gmail.com>: ``I still
168 ;; believe that the right way is to use the plaintext output from GnuPG as
169 ;; it is, and mml2015-extract-cleartext-signature is just a kludge for
170 ;; misdesigned libraries like PGG, which have no ability to do that. So, I
171 ;; think it should not have descriptive documentation.''
172 ;;
173 ;; This function doesn't handle NotDashEscaped correctly. EasyPG handles it
174 ;; correctly.
175 ;; http://thread.gmane.org/gmane.emacs.gnus.general/66062/focus=66082
176 ;; http://thread.gmane.org/gmane.emacs.gnus.general/65087/focus=65109
177 (goto-char (point-min))
178 (forward-line)
179 ;; We need to be careful not to strip beyond the armor headers.
180 ;; Previously, an attacker could replace the text inside our
181 ;; markup with trailing garbage by injecting whitespace into the
182 ;; message.
183 (while (looking-at "Hash:") ; The only header allowed in cleartext
184 (forward-line)) ; signatures according to RFC2440.
185 (when (looking-at "[\t ]*$")
186 (forward-line))
187 (delete-region (point-min) (point))
188 (if (re-search-forward "^-----BEGIN PGP SIGNATURE-----" nil t)
189 (delete-region (match-beginning 0) (point-max)))
190 (goto-char (point-min))
191 (while (re-search-forward "^- " nil t)
192 (replace-match "" t t)
193 (forward-line 1)))
194
195 ;;; mailcrypt wrapper
196
197 (autoload 'mailcrypt-decrypt "mailcrypt")
198 (autoload 'mailcrypt-verify "mailcrypt")
199 (autoload 'mc-pgp-always-sign "mailcrypt")
200 (autoload 'mc-encrypt-generic "mc-toplev")
201 (autoload 'mc-cleanup-recipient-headers "mc-toplev")
202 (autoload 'mc-sign-generic "mc-toplev")
203
204 (defvar mml2015-decrypt-function 'mailcrypt-decrypt)
205 (defvar mml2015-verify-function 'mailcrypt-verify)
206
207 (defun mml2015-format-error (err)
208 (if (stringp (cadr err))
209 (cadr err)
210 (format "%S" (cdr err))))
211
212 (defun mml2015-mailcrypt-decrypt (handle ctl)
213 (catch 'error
214 (let (child handles result)
215 (unless (setq child (mm-find-part-by-type
216 (cdr handle)
217 "application/octet-stream" nil t))
218 (mm-set-handle-multipart-parameter
219 mm-security-handle 'gnus-info "Corrupted")
220 (throw 'error handle))
221 (with-temp-buffer
222 (mm-insert-part child)
223 (setq result
224 (condition-case err
225 (funcall mml2015-decrypt-function)
226 (error
227 (mm-set-handle-multipart-parameter
228 mm-security-handle 'gnus-details (mml2015-format-error err))
229 nil)
230 (quit
231 (mm-set-handle-multipart-parameter
232 mm-security-handle 'gnus-details "Quit.")
233 nil)))
234 (unless (car result)
235 (mm-set-handle-multipart-parameter
236 mm-security-handle 'gnus-info "Failed")
237 (throw 'error handle))
238 (setq handles (mm-dissect-buffer t)))
239 (mm-destroy-parts handle)
240 (mm-set-handle-multipart-parameter
241 mm-security-handle 'gnus-info
242 (concat "OK"
243 (let ((sig (with-current-buffer mml2015-result-buffer
244 (mml2015-gpg-extract-signature-details))))
245 (concat ", Signer: " sig))))
246 (if (listp (car handles))
247 handles
248 (list handles)))))
249
250 (defun mml2015-gpg-pretty-print-fpr (fingerprint)
251 (let* ((result "")
252 (fpr-length (string-width fingerprint))
253 (n-slice 0)
254 slice)
255 (setq fingerprint (string-to-list fingerprint))
256 (while fingerprint
257 (setq fpr-length (- fpr-length 4))
258 (setq slice (butlast fingerprint fpr-length))
259 (setq fingerprint (nthcdr 4 fingerprint))
260 (setq n-slice (1+ n-slice))
261 (setq result
262 (concat
263 result
264 (case n-slice
265 (1 slice)
266 (otherwise (concat " " slice))))))
267 result))
268
269 (defun mml2015-gpg-extract-signature-details ()
270 (goto-char (point-min))
271 (let* ((expired (re-search-forward
272 "^\\[GNUPG:\\] SIGEXPIRED$"
273 nil t))
274 (signer (and (re-search-forward
275 "^\\[GNUPG:\\] GOODSIG \\([0-9A-Za-z]*\\) \\(.*\\)$"
276 nil t)
277 (cons (match-string 1) (match-string 2))))
278 (fprint (and (re-search-forward
279 "^\\[GNUPG:\\] VALIDSIG \\([0-9a-zA-Z]*\\) "
280 nil t)
281 (match-string 1)))
282 (trust (and (re-search-forward
283 "^\\[GNUPG:\\] \\(TRUST_.*\\)$"
284 nil t)
285 (match-string 1)))
286 (trust-good-enough-p
287 (cdr (assoc trust mml2015-unabbrev-trust-alist))))
288 (cond ((and signer fprint)
289 (concat (cdr signer)
290 (unless trust-good-enough-p
291 (concat "\nUntrusted, Fingerprint: "
292 (mml2015-gpg-pretty-print-fpr fprint)))
293 (when expired
294 (format "\nWARNING: Signature from expired key (%s)"
295 (car signer)))))
296 ((re-search-forward
297 "^\\(gpg: \\)?Good signature from \"\\(.*\\)\"$" nil t)
298 (match-string 2))
299 (t
300 "From unknown user"))))
301
302 (defun mml2015-mailcrypt-clear-decrypt ()
303 (let (result)
304 (setq result
305 (condition-case err
306 (funcall mml2015-decrypt-function)
307 (error
308 (mm-set-handle-multipart-parameter
309 mm-security-handle 'gnus-details (mml2015-format-error err))
310 nil)
311 (quit
312 (mm-set-handle-multipart-parameter
313 mm-security-handle 'gnus-details "Quit.")
314 nil)))
315 (if (car result)
316 (mm-set-handle-multipart-parameter
317 mm-security-handle 'gnus-info "OK")
318 (mm-set-handle-multipart-parameter
319 mm-security-handle 'gnus-info "Failed"))))
320
321 (defun mml2015-fix-micalg (alg)
322 (and alg
323 ;; Mutt/1.2.5i has seen sending micalg=php-sha1
324 (upcase (if (string-match "^p[gh]p-" alg)
325 (substring alg (match-end 0))
326 alg))))
327
328 (defun mml2015-mailcrypt-verify (handle ctl)
329 (catch 'error
330 (let (part)
331 (unless (setq part (mm-find-raw-part-by-type
332 ctl (or (mm-handle-multipart-ctl-parameter
333 ctl 'protocol)
334 "application/pgp-signature")
335 t))
336 (mm-set-handle-multipart-parameter
337 mm-security-handle 'gnus-info "Corrupted")
338 (throw 'error handle))
339 (with-temp-buffer
340 (insert "-----BEGIN PGP SIGNED MESSAGE-----\n")
341 (insert (format "Hash: %s\n\n"
342 (or (mml2015-fix-micalg
343 (mm-handle-multipart-ctl-parameter
344 ctl 'micalg))
345 "SHA1")))
346 (save-restriction
347 (narrow-to-region (point) (point))
348 (insert part "\n")
349 (goto-char (point-min))
350 (while (not (eobp))
351 (if (looking-at "^-")
352 (insert "- "))
353 (forward-line)))
354 (unless (setq part (mm-find-part-by-type
355 (cdr handle) "application/pgp-signature" nil t))
356 (mm-set-handle-multipart-parameter
357 mm-security-handle 'gnus-info "Corrupted")
358 (throw 'error handle))
359 (save-restriction
360 (narrow-to-region (point) (point))
361 (mm-insert-part part)
362 (goto-char (point-min))
363 (if (re-search-forward "^-----BEGIN PGP [^-]+-----\r?$" nil t)
364 (replace-match "-----BEGIN PGP SIGNATURE-----" t t))
365 (if (re-search-forward "^-----END PGP [^-]+-----\r?$" nil t)
366 (replace-match "-----END PGP SIGNATURE-----" t t)))
367 (let ((mc-gpg-debug-buffer (get-buffer-create " *gnus gpg debug*")))
368 (unless (condition-case err
369 (prog1
370 (funcall mml2015-verify-function)
371 (if (get-buffer " *mailcrypt stderr temp")
372 (mm-set-handle-multipart-parameter
373 mm-security-handle 'gnus-details
374 (with-current-buffer " *mailcrypt stderr temp"
375 (buffer-string))))
376 (if (get-buffer " *mailcrypt stdout temp")
377 (kill-buffer " *mailcrypt stdout temp"))
378 (if (get-buffer " *mailcrypt stderr temp")
379 (kill-buffer " *mailcrypt stderr temp"))
380 (if (get-buffer " *mailcrypt status temp")
381 (kill-buffer " *mailcrypt status temp"))
382 (if (get-buffer mc-gpg-debug-buffer)
383 (kill-buffer mc-gpg-debug-buffer)))
384 (error
385 (mm-set-handle-multipart-parameter
386 mm-security-handle 'gnus-details (mml2015-format-error err))
387 nil)
388 (quit
389 (mm-set-handle-multipart-parameter
390 mm-security-handle 'gnus-details "Quit.")
391 nil))
392 (mm-set-handle-multipart-parameter
393 mm-security-handle 'gnus-info "Failed")
394 (throw 'error handle))))
395 (mm-set-handle-multipart-parameter
396 mm-security-handle 'gnus-info "OK")
397 handle)))
398
399 (defun mml2015-mailcrypt-clear-verify ()
400 (let ((mc-gpg-debug-buffer (get-buffer-create " *gnus gpg debug*")))
401 (if (condition-case err
402 (prog1
403 (funcall mml2015-verify-function)
404 (if (get-buffer " *mailcrypt stderr temp")
405 (mm-set-handle-multipart-parameter
406 mm-security-handle 'gnus-details
407 (with-current-buffer " *mailcrypt stderr temp"
408 (buffer-string))))
409 (if (get-buffer " *mailcrypt stdout temp")
410 (kill-buffer " *mailcrypt stdout temp"))
411 (if (get-buffer " *mailcrypt stderr temp")
412 (kill-buffer " *mailcrypt stderr temp"))
413 (if (get-buffer " *mailcrypt status temp")
414 (kill-buffer " *mailcrypt status temp"))
415 (if (get-buffer mc-gpg-debug-buffer)
416 (kill-buffer mc-gpg-debug-buffer)))
417 (error
418 (mm-set-handle-multipart-parameter
419 mm-security-handle 'gnus-details (mml2015-format-error err))
420 nil)
421 (quit
422 (mm-set-handle-multipart-parameter
423 mm-security-handle 'gnus-details "Quit.")
424 nil))
425 (mm-set-handle-multipart-parameter
426 mm-security-handle 'gnus-info "OK")
427 (mm-set-handle-multipart-parameter
428 mm-security-handle 'gnus-info "Failed")))
429 (mml2015-extract-cleartext-signature))
430
431 (defun mml2015-mailcrypt-sign (cont)
432 (mc-sign-generic (message-options-get 'message-sender)
433 nil nil nil nil)
434 (let ((boundary (mml-compute-boundary cont))
435 hash point)
436 (goto-char (point-min))
437 (unless (re-search-forward "^-----BEGIN PGP SIGNED MESSAGE-----\r?$" nil t)
438 (error "Cannot find signed begin line"))
439 (goto-char (match-beginning 0))
440 (forward-line 1)
441 (unless (looking-at "Hash:[ \t]*\\([a-zA-Z0-9]+\\)")
442 (error "Cannot not find PGP hash"))
443 (setq hash (match-string 1))
444 (unless (re-search-forward "^$" nil t)
445 (error "Cannot not find PGP message"))
446 (forward-line 1)
447 (delete-region (point-min) (point))
448 (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"
449 boundary))
450 (insert (format "\tmicalg=pgp-%s; protocol=\"application/pgp-signature\"\n"
451 (downcase hash)))
452 (insert (format "\n--%s\n" boundary))
453 (setq point (point))
454 (goto-char (point-max))
455 (unless (re-search-backward "^-----END PGP SIGNATURE-----\r?$" nil t)
456 (error "Cannot find signature part"))
457 (replace-match "-----END PGP MESSAGE-----" t t)
458 (goto-char (match-beginning 0))
459 (unless (re-search-backward "^-----BEGIN PGP SIGNATURE-----\r?$"
460 nil t)
461 (error "Cannot find signature part"))
462 (replace-match "-----BEGIN PGP MESSAGE-----" t t)
463 (goto-char (match-beginning 0))
464 (save-restriction
465 (narrow-to-region point (point))
466 (goto-char point)
467 (while (re-search-forward "^- -" nil t)
468 (replace-match "-" t t))
469 (goto-char (point-max)))
470 (insert (format "--%s\n" boundary))
471 (insert "Content-Type: application/pgp-signature\n\n")
472 (goto-char (point-max))
473 (insert (format "--%s--\n" boundary))
474 (goto-char (point-max))))
475
476 ;; We require mm-decode, which requires mm-bodies, which autoloads
477 ;; message-options-get (!).
478 (declare-function message-options-set "message" (symbol value))
479
480 (defun mml2015-mailcrypt-encrypt (cont &optional sign)
481 (let ((mc-pgp-always-sign
482 (or mc-pgp-always-sign
483 sign
484 (eq t (or (message-options-get 'message-sign-encrypt)
485 (message-options-set
486 'message-sign-encrypt
487 (or (y-or-n-p "Sign the message? ")
488 'not))))
489 'never)))
490 (insert
491 (with-temp-buffer
492 (set-buffer-multibyte nil)
493 (mc-encrypt-generic
494 (or (message-options-get 'message-recipients)
495 (message-options-set 'message-recipients
496 (mc-cleanup-recipient-headers
497 (read-string "Recipients: "))))
498 nil nil nil
499 (message-options-get 'message-sender))
500 (buffer-string))))
501 (goto-char (point-min))
502 (unless (looking-at "-----BEGIN PGP MESSAGE-----")
503 (error "Fail to encrypt the message"))
504 (let ((boundary (mml-compute-boundary cont)))
505 (insert (format "Content-Type: multipart/encrypted; boundary=\"%s\";\n"
506 boundary))
507 (insert "\tprotocol=\"application/pgp-encrypted\"\n\n")
508 (insert (format "--%s\n" boundary))
509 (insert "Content-Type: application/pgp-encrypted\n\n")
510 (insert "Version: 1\n\n")
511 (insert (format "--%s\n" boundary))
512 (insert "Content-Type: application/octet-stream\n\n")
513 (goto-char (point-max))
514 (insert (format "--%s--\n" boundary))
515 (goto-char (point-max))))
516
517 ;;; pgg wrapper
518
519 (defvar pgg-default-user-id)
520 (defvar pgg-errors-buffer)
521 (defvar pgg-output-buffer)
522
523 (autoload 'pgg-decrypt-region "pgg")
524 (autoload 'pgg-verify-region "pgg")
525 (autoload 'pgg-sign-region "pgg")
526 (autoload 'pgg-encrypt-region "pgg")
527 (autoload 'pgg-parse-armor "pgg-parse")
528
529 (defun mml2015-pgg-decrypt (handle ctl)
530 (catch 'error
531 (let ((pgg-errors-buffer mml2015-result-buffer)
532 child handles result decrypt-status)
533 (unless (setq child (mm-find-part-by-type
534 (cdr handle)
535 "application/octet-stream" nil t))
536 (mm-set-handle-multipart-parameter
537 mm-security-handle 'gnus-info "Corrupted")
538 (throw 'error handle))
539 (with-temp-buffer
540 (mm-insert-part child)
541 (if (condition-case err
542 (prog1
543 (pgg-decrypt-region (point-min) (point-max))
544 (setq decrypt-status
545 (with-current-buffer mml2015-result-buffer
546 (buffer-string)))
547 (mm-set-handle-multipart-parameter
548 mm-security-handle 'gnus-details
549 decrypt-status))
550 (error
551 (mm-set-handle-multipart-parameter
552 mm-security-handle 'gnus-details (mml2015-format-error err))
553 nil)
554 (quit
555 (mm-set-handle-multipart-parameter
556 mm-security-handle 'gnus-details "Quit.")
557 nil))
558 (with-current-buffer pgg-output-buffer
559 (goto-char (point-min))
560 (while (search-forward "\r\n" nil t)
561 (replace-match "\n" t t))
562 (setq handles (mm-dissect-buffer t))
563 (mm-destroy-parts handle)
564 (mm-set-handle-multipart-parameter
565 mm-security-handle 'gnus-info "OK")
566 (mm-set-handle-multipart-parameter
567 mm-security-handle 'gnus-details
568 (concat decrypt-status
569 (when (stringp (car handles))
570 "\n" (mm-handle-multipart-ctl-parameter
571 handles 'gnus-details))))
572 (if (listp (car handles))
573 handles
574 (list handles)))
575 (mm-set-handle-multipart-parameter
576 mm-security-handle 'gnus-info "Failed")
577 (throw 'error handle))))))
578
579 (defun mml2015-pgg-clear-decrypt ()
580 (let ((pgg-errors-buffer mml2015-result-buffer))
581 (if (prog1
582 (pgg-decrypt-region (point-min) (point-max))
583 (mm-set-handle-multipart-parameter
584 mm-security-handle 'gnus-details
585 (with-current-buffer mml2015-result-buffer
586 (buffer-string))))
587 (progn
588 (erase-buffer)
589 ;; Treat data which pgg returns as a unibyte string.
590 (mm-disable-multibyte)
591 (insert-buffer-substring pgg-output-buffer)
592 (goto-char (point-min))
593 (while (search-forward "\r\n" nil t)
594 (replace-match "\n" t t))
595 (mm-set-handle-multipart-parameter
596 mm-security-handle 'gnus-info "OK"))
597 (mm-set-handle-multipart-parameter
598 mm-security-handle 'gnus-info "Failed"))))
599
600 (defun mml2015-pgg-verify (handle ctl)
601 (let ((pgg-errors-buffer mml2015-result-buffer)
602 signature-file part signature)
603 (if (or (null (setq part (mm-find-raw-part-by-type
604 ctl (or (mm-handle-multipart-ctl-parameter
605 ctl 'protocol)
606 "application/pgp-signature")
607 t)))
608 (null (setq signature (mm-find-part-by-type
609 (cdr handle) "application/pgp-signature" nil t))))
610 (progn
611 (mm-set-handle-multipart-parameter
612 mm-security-handle 'gnus-info "Corrupted")
613 handle)
614 (with-temp-buffer
615 (insert part)
616 ;; Convert <LF> to <CR><LF> in signed text. If --textmode is
617 ;; specified when signing, the conversion is not necessary.
618 (goto-char (point-min))
619 (end-of-line)
620 (while (not (eobp))
621 (unless (eq (char-before) ?\r)
622 (insert "\r"))
623 (forward-line)
624 (end-of-line))
625 (with-temp-file (setq signature-file (make-temp-file "pgg"))
626 (mm-insert-part signature))
627 (if (condition-case err
628 (prog1
629 (pgg-verify-region (point-min) (point-max)
630 signature-file t)
631 (goto-char (point-min))
632 (while (search-forward "\r\n" nil t)
633 (replace-match "\n" t t))
634 (mm-set-handle-multipart-parameter
635 mm-security-handle 'gnus-details
636 (concat (with-current-buffer pgg-output-buffer
637 (buffer-string))
638 (with-current-buffer pgg-errors-buffer
639 (buffer-string)))))
640 (error
641 (mm-set-handle-multipart-parameter
642 mm-security-handle 'gnus-details (mml2015-format-error err))
643 nil)
644 (quit
645 (mm-set-handle-multipart-parameter
646 mm-security-handle 'gnus-details "Quit.")
647 nil))
648 (progn
649 (delete-file signature-file)
650 (mm-set-handle-multipart-parameter
651 mm-security-handle 'gnus-info
652 (with-current-buffer pgg-errors-buffer
653 (mml2015-gpg-extract-signature-details))))
654 (delete-file signature-file)
655 (mm-set-handle-multipart-parameter
656 mm-security-handle 'gnus-info "Failed")))))
657 handle)
658
659 (defun mml2015-pgg-clear-verify ()
660 (let ((pgg-errors-buffer mml2015-result-buffer)
661 (text (buffer-string))
662 (coding-system buffer-file-coding-system))
663 (if (condition-case err
664 (prog1
665 (mm-with-unibyte-buffer
666 (insert (encode-coding-string text coding-system))
667 (pgg-verify-region (point-min) (point-max) nil t))
668 (goto-char (point-min))
669 (while (search-forward "\r\n" nil t)
670 (replace-match "\n" t t))
671 (mm-set-handle-multipart-parameter
672 mm-security-handle 'gnus-details
673 (concat (with-current-buffer pgg-output-buffer
674 (buffer-string))
675 (with-current-buffer pgg-errors-buffer
676 (buffer-string)))))
677 (error
678 (mm-set-handle-multipart-parameter
679 mm-security-handle 'gnus-details (mml2015-format-error err))
680 nil)
681 (quit
682 (mm-set-handle-multipart-parameter
683 mm-security-handle 'gnus-details "Quit.")
684 nil))
685 (mm-set-handle-multipart-parameter
686 mm-security-handle 'gnus-info
687 (with-current-buffer pgg-errors-buffer
688 (mml2015-gpg-extract-signature-details)))
689 (mm-set-handle-multipart-parameter
690 mm-security-handle 'gnus-info "Failed")))
691 (mml2015-extract-cleartext-signature))
692
693 (defun mml2015-pgg-sign (cont)
694 (let ((pgg-errors-buffer mml2015-result-buffer)
695 (boundary (mml-compute-boundary cont))
696 (pgg-default-user-id (or (message-options-get 'mml-sender)
697 pgg-default-user-id))
698 (pgg-text-mode t)
699 entry)
700 (unless (pgg-sign-region (point-min) (point-max))
701 (pop-to-buffer mml2015-result-buffer)
702 (error "Sign error"))
703 (goto-char (point-min))
704 (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"
705 boundary))
706 (if (setq entry (assq 2 (pgg-parse-armor
707 (with-current-buffer pgg-output-buffer
708 (buffer-string)))))
709 (setq entry (assq 'hash-algorithm (cdr entry))))
710 (insert (format "\tmicalg=%s; "
711 (if (cdr entry)
712 (downcase (format "pgp-%s" (cdr entry)))
713 "pgp-sha1")))
714 (insert "protocol=\"application/pgp-signature\"\n")
715 (insert (format "\n--%s\n" boundary))
716 (goto-char (point-max))
717 (insert (format "\n--%s\n" boundary))
718 (insert "Content-Type: application/pgp-signature\n\n")
719 (insert-buffer-substring pgg-output-buffer)
720 (goto-char (point-max))
721 (insert (format "--%s--\n" boundary))
722 (goto-char (point-max))))
723
724 (defun mml2015-pgg-encrypt (cont &optional sign)
725 (let ((pgg-errors-buffer mml2015-result-buffer)
726 (pgg-text-mode t)
727 (boundary (mml-compute-boundary cont)))
728 (unless (pgg-encrypt-region (point-min) (point-max)
729 (split-string
730 (or
731 (message-options-get 'message-recipients)
732 (message-options-set 'message-recipients
733 (read-string "Recipients: ")))
734 "[ \f\t\n\r\v,]+")
735 sign)
736 (pop-to-buffer mml2015-result-buffer)
737 (error "Encrypt error"))
738 (delete-region (point-min) (point-max))
739 (goto-char (point-min))
740 (insert (format "Content-Type: multipart/encrypted; boundary=\"%s\";\n"
741 boundary))
742 (insert "\tprotocol=\"application/pgp-encrypted\"\n\n")
743 (insert (format "--%s\n" boundary))
744 (insert "Content-Type: application/pgp-encrypted\n\n")
745 (insert "Version: 1\n\n")
746 (insert (format "--%s\n" boundary))
747 (insert "Content-Type: application/octet-stream\n\n")
748 (insert-buffer-substring pgg-output-buffer)
749 (goto-char (point-max))
750 (insert (format "--%s--\n" boundary))
751 (goto-char (point-max))))
752
753 ;;; epg wrapper
754
755 (defvar epg-user-id-alist)
756 (defvar epg-digest-algorithm-alist)
757 (defvar epg-gpg-program)
758 (defvar inhibit-redisplay)
759
760 (autoload 'epg-make-context "epg")
761 (autoload 'epg-context-set-armor "epg")
762 (autoload 'epg-context-set-textmode "epg")
763 (autoload 'epg-context-set-signers "epg")
764 (autoload 'epg-context-result-for "epg")
765 (autoload 'epg-new-signature-digest-algorithm "epg")
766 (autoload 'epg-list-keys "epg")
767 (autoload 'epg-decrypt-string "epg")
768 (autoload 'epg-verify-string "epg")
769 (autoload 'epg-sign-string "epg")
770 (autoload 'epg-encrypt-string "epg")
771 (autoload 'epg-passphrase-callback-function "epg")
772 (autoload 'epg-context-set-passphrase-callback "epg")
773 (autoload 'epg-key-sub-key-list "epg")
774 (autoload 'epg-sub-key-capability "epg")
775 (autoload 'epg-sub-key-validity "epg")
776 (autoload 'epg-sub-key-fingerprint "epg")
777 (autoload 'epg-signature-key-id "epg")
778 (autoload 'epg-signature-to-string "epg")
779 (autoload 'epg-key-user-id-list "epg")
780 (autoload 'epg-user-id-string "epg")
781 (autoload 'epg-user-id-validity "epg")
782 (autoload 'epg-configuration "epg-config")
783 (autoload 'epg-expand-group "epg-config")
784 (autoload 'epa-select-keys "epa")
785
786 (defun mml2015-epg-key-image (key-id)
787 "Return the image of a key, if any"
788 (with-temp-buffer
789 (set-buffer-multibyte nil)
790 (let* ((coding-system-for-write 'binary)
791 (coding-system-for-read 'binary)
792 (data (shell-command-to-string
793 (format "%s --list-options no-show-photos --attribute-fd 3 --list-keys %s 3>&1 >/dev/null 2>&1"
794 (shell-quote-argument epg-gpg-program) key-id))))
795 (when (> (length data) 0)
796 (insert (substring data 16))
797 (condition-case nil
798 (gnus-create-image (buffer-string) nil t)
799 (error))))))
800
801 (autoload 'gnus-rescale-image "gnus-util")
802
803 (defun mml2015-epg-key-image-to-string (key-id)
804 "Return a string with the image of a key, if any"
805 (let ((key-image (mml2015-epg-key-image key-id)))
806 (if (not key-image)
807 ""
808 (condition-case error
809 (let ((result " "))
810 (put-text-property
811 1 2 'display
812 (gnus-rescale-image key-image
813 (cons mml2015-maximum-key-image-dimension
814 mml2015-maximum-key-image-dimension))
815 result)
816 result)
817 (error "")))))
818
819 (defun mml2015-epg-signature-to-string (signature)
820 (concat (epg-signature-to-string signature)
821 (when mml2015-display-key-image
822 (mml2015-epg-key-image-to-string (epg-signature-key-id signature)))))
823
824 (defun mml2015-epg-verify-result-to-string (verify-result)
825 (mapconcat #'mml2015-epg-signature-to-string verify-result "\n"))
826
827 (defun mml2015-epg-decrypt (handle ctl)
828 (catch 'error
829 (let ((inhibit-redisplay t)
830 context plain child handles result decrypt-status)
831 (unless (setq child (mm-find-part-by-type
832 (cdr handle)
833 "application/octet-stream" nil t))
834 (mm-set-handle-multipart-parameter
835 mm-security-handle 'gnus-info "Corrupted")
836 (throw 'error handle))
837 (setq context (epg-make-context))
838 (if (or mml2015-cache-passphrase mml-secure-cache-passphrase)
839 (epg-context-set-passphrase-callback
840 context
841 (cons 'mml-secure-passphrase-callback 'OpenPGP)))
842 (condition-case error
843 (setq plain (epg-decrypt-string context (mm-get-part child))
844 mml-secure-secret-key-id-list nil)
845 (error
846 (mml-secure-clear-secret-key-id-list)
847 (mm-set-handle-multipart-parameter
848 mm-security-handle 'gnus-info "Failed")
849 (if (eq (car error) 'quit)
850 (mm-set-handle-multipart-parameter
851 mm-security-handle 'gnus-details "Quit.")
852 (mm-set-handle-multipart-parameter
853 mm-security-handle 'gnus-details (mml2015-format-error error)))
854 (throw 'error handle)))
855 (with-temp-buffer
856 (insert plain)
857 (goto-char (point-min))
858 (while (search-forward "\r\n" nil t)
859 (replace-match "\n" t t))
860 (setq handles (mm-dissect-buffer t))
861 (mm-destroy-parts handle)
862 (if (epg-context-result-for context 'verify)
863 (mm-set-handle-multipart-parameter
864 mm-security-handle 'gnus-info
865 (concat "OK\n"
866 (mml2015-epg-verify-result-to-string
867 (epg-context-result-for context 'verify))))
868 (mm-set-handle-multipart-parameter
869 mm-security-handle 'gnus-info "OK"))
870 (if (stringp (car handles))
871 (mm-set-handle-multipart-parameter
872 mm-security-handle 'gnus-details
873 (mm-handle-multipart-ctl-parameter handles 'gnus-details))))
874 (if (listp (car handles))
875 handles
876 (list handles)))))
877
878 (defun mml2015-epg-clear-decrypt ()
879 (let ((inhibit-redisplay t)
880 (context (epg-make-context))
881 plain)
882 (if (or mml2015-cache-passphrase mml-secure-cache-passphrase)
883 (epg-context-set-passphrase-callback
884 context
885 (cons 'mml-secure-passphrase-callback 'OpenPGP)))
886 (condition-case error
887 (setq plain (epg-decrypt-string context (buffer-string))
888 mml-secure-secret-key-id-list nil)
889 (error
890 (mml-secure-clear-secret-key-id-list)
891 (mm-set-handle-multipart-parameter
892 mm-security-handle 'gnus-info "Failed")
893 (if (eq (car error) 'quit)
894 (mm-set-handle-multipart-parameter
895 mm-security-handle 'gnus-details "Quit.")
896 (mm-set-handle-multipart-parameter
897 mm-security-handle 'gnus-details (mml2015-format-error error)))))
898 (when plain
899 (erase-buffer)
900 ;; Treat data which epg returns as a unibyte string.
901 (mm-disable-multibyte)
902 (insert plain)
903 (goto-char (point-min))
904 (while (search-forward "\r\n" nil t)
905 (replace-match "\n" t t))
906 (mm-set-handle-multipart-parameter
907 mm-security-handle 'gnus-info "OK")
908 (if (epg-context-result-for context 'verify)
909 (mm-set-handle-multipart-parameter
910 mm-security-handle 'gnus-details
911 (mml2015-epg-verify-result-to-string
912 (epg-context-result-for context 'verify)))))))
913
914 (defun mml2015-epg-verify (handle ctl)
915 (catch 'error
916 (let ((inhibit-redisplay t)
917 context plain signature-file part signature)
918 (when (or (null (setq part (mm-find-raw-part-by-type
919 ctl (or (mm-handle-multipart-ctl-parameter
920 ctl 'protocol)
921 "application/pgp-signature")
922 t)))
923 (null (setq signature (mm-find-part-by-type
924 (cdr handle) "application/pgp-signature"
925 nil t))))
926 (mm-set-handle-multipart-parameter
927 mm-security-handle 'gnus-info "Corrupted")
928 (throw 'error handle))
929 (setq part (replace-regexp-in-string "\n" "\r\n" part)
930 signature (mm-get-part signature)
931 context (epg-make-context))
932 (condition-case error
933 (setq plain (epg-verify-string context signature part))
934 (error
935 (mm-set-handle-multipart-parameter
936 mm-security-handle 'gnus-info "Failed")
937 (if (eq (car error) 'quit)
938 (mm-set-handle-multipart-parameter
939 mm-security-handle 'gnus-details "Quit.")
940 (mm-set-handle-multipart-parameter
941 mm-security-handle 'gnus-details (mml2015-format-error error)))
942 (throw 'error handle)))
943 (mm-set-handle-multipart-parameter
944 mm-security-handle 'gnus-info
945 (mml2015-epg-verify-result-to-string
946 (epg-context-result-for context 'verify)))
947 handle)))
948
949 (defun mml2015-epg-clear-verify ()
950 (let ((inhibit-redisplay t)
951 (context (epg-make-context))
952 (signature (encode-coding-string (buffer-string)
953 coding-system-for-write))
954 plain)
955 (condition-case error
956 (setq plain (epg-verify-string context signature))
957 (error
958 (mm-set-handle-multipart-parameter
959 mm-security-handle 'gnus-info "Failed")
960 (if (eq (car error) 'quit)
961 (mm-set-handle-multipart-parameter
962 mm-security-handle 'gnus-details "Quit.")
963 (mm-set-handle-multipart-parameter
964 mm-security-handle 'gnus-details (mml2015-format-error error)))))
965 (if plain
966 (progn
967 (mm-set-handle-multipart-parameter
968 mm-security-handle 'gnus-info
969 (mml2015-epg-verify-result-to-string
970 (epg-context-result-for context 'verify)))
971 (delete-region (point-min) (point-max))
972 (insert (decode-coding-string plain coding-system-for-read)))
973 (mml2015-extract-cleartext-signature))))
974
975 (defun mml2015-epg-sign (cont)
976 (let ((inhibit-redisplay t)
977 (boundary (mml-compute-boundary cont)))
978 ;; Signed data must end with a newline (RFC 3156, 5).
979 (goto-char (point-max))
980 (unless (bolp)
981 (insert "\n"))
982 (let* ((pair (mml-secure-epg-sign 'OpenPGP t))
983 (signature (car pair))
984 (micalg (cdr pair)))
985 (goto-char (point-min))
986 (insert (format "Content-Type: multipart/signed; boundary=\"%s\";\n"
987 boundary))
988 (if micalg
989 (insert (format "\tmicalg=pgp-%s; "
990 (downcase
991 (cdr (assq micalg
992 epg-digest-algorithm-alist))))))
993 (insert "protocol=\"application/pgp-signature\"\n")
994 (insert (format "\n--%s\n" boundary))
995 (goto-char (point-max))
996 (insert (format "\n--%s\n" boundary))
997 (insert "Content-Type: application/pgp-signature; name=\"signature.asc\"\n\n")
998 (insert signature)
999 (goto-char (point-max))
1000 (insert (format "--%s--\n" boundary))
1001 (goto-char (point-max)))))
1002
1003 (defun mml2015-epg-encrypt (cont &optional sign)
1004 (let* ((inhibit-redisplay t)
1005 (boundary (mml-compute-boundary cont))
1006 (cipher (mml-secure-epg-encrypt 'OpenPGP cont sign)))
1007 (delete-region (point-min) (point-max))
1008 (goto-char (point-min))
1009 (insert (format "Content-Type: multipart/encrypted; boundary=\"%s\";\n"
1010 boundary))
1011 (insert "\tprotocol=\"application/pgp-encrypted\"\n\n")
1012 (insert (format "--%s\n" boundary))
1013 (insert "Content-Type: application/pgp-encrypted\n\n")
1014 (insert "Version: 1\n\n")
1015 (insert (format "--%s\n" boundary))
1016 (insert "Content-Type: application/octet-stream\n\n")
1017 (insert cipher)
1018 (goto-char (point-max))
1019 (insert (format "--%s--\n" boundary))
1020 (goto-char (point-max))))
1021
1022 ;;; General wrapper
1023
1024 (autoload 'gnus-buffer-live-p "gnus-util")
1025 (autoload 'gnus-get-buffer-create "gnus")
1026
1027 (defun mml2015-clean-buffer ()
1028 (if (gnus-buffer-live-p mml2015-result-buffer)
1029 (with-current-buffer mml2015-result-buffer
1030 (erase-buffer)
1031 t)
1032 (setq mml2015-result-buffer
1033 (gnus-get-buffer-create " *MML2015 Result*"))
1034 nil))
1035
1036 (defsubst mml2015-clear-decrypt-function ()
1037 (nth 6 (assq mml2015-use mml2015-function-alist)))
1038
1039 (defsubst mml2015-clear-verify-function ()
1040 (nth 5 (assq mml2015-use mml2015-function-alist)))
1041
1042 ;;;###autoload
1043 (defun mml2015-decrypt (handle ctl)
1044 (mml2015-clean-buffer)
1045 (let ((func (nth 4 (assq mml2015-use mml2015-function-alist))))
1046 (if func
1047 (funcall func handle ctl)
1048 handle)))
1049
1050 ;;;###autoload
1051 (defun mml2015-decrypt-test (handle ctl)
1052 mml2015-use)
1053
1054 ;;;###autoload
1055 (defun mml2015-verify (handle ctl)
1056 (mml2015-clean-buffer)
1057 (let ((func (nth 3 (assq mml2015-use mml2015-function-alist))))
1058 (if func
1059 (funcall func handle ctl)
1060 handle)))
1061
1062 ;;;###autoload
1063 (defun mml2015-verify-test (handle ctl)
1064 mml2015-use)
1065
1066 ;;;###autoload
1067 (defun mml2015-encrypt (cont &optional sign)
1068 (mml2015-clean-buffer)
1069 (let ((func (nth 2 (assq mml2015-use mml2015-function-alist))))
1070 (if func
1071 (funcall func cont sign)
1072 (error "Cannot find encrypt function"))))
1073
1074 ;;;###autoload
1075 (defun mml2015-sign (cont)
1076 (mml2015-clean-buffer)
1077 (let ((func (nth 1 (assq mml2015-use mml2015-function-alist))))
1078 (if func
1079 (funcall func cont)
1080 (error "Cannot find sign function"))))
1081
1082 ;;;###autoload
1083 (defun mml2015-self-encrypt ()
1084 (mml2015-encrypt nil))
1085
1086 (provide 'mml2015)
1087
1088 ;;; mml2015.el ends here