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