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