1 ;; mc-gpg.el, GPG support for Mailcrypt
2 ;; Copyright (C) 1995 Jin Choi <jin@atype.com>
3 ;; Patrick LoPresti <patl@lcs.mit.edu>
4 ;; 1998 Brian Warner <warner@lothar.com>
6 ;; $Id: mc-gpg.el,v 1.6 2002-10-04 01:58:28 youngs Exp $
9 ;; This file is intended to be used with GNU Emacs.
11 ;; This program 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 2, or (at your option)
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to
23 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
30 ; #mc-gpg-encrypt-region
31 ; need to deal with untrusted keys, missing keys (offer to fetch), --throw
32 ; #mc-gpg-decrypt-region [anything not clearsigned] (a,as,ae,ase)
33 ; need to implement signature-key fetch, ponder --throw-keyid case
34 ; keys without passphrases, sigs with bad algorithms (ignore sig? warn?)
35 ; #mc-gpg-sign-region (clearsign/notclearsign)
36 ; #mc-gpg-verify-region [clearsigned only] (ok/badsig/missingkey/corruptmsg)
37 ; #mc-gpg-insert-public-key (comment, altkeyring)
38 ; #mc-gpg-snarf-keys (one, multiple, old, corrupt)
39 ; key fetching (is there a GPG key server yet?)
40 ; clean up use of buffers, #kill off old tmp buffers
41 ; in verify-region, print date of signature too
42 ; ~maybe have bad-signature message print keyid/date? (no, sig is invalid,
43 ; ~ anything other than its invalidity is misleading)
44 ; make messages shorter (get it all to fit in echo area)
46 ; enhancements I'd like to add
47 ; trustdb status reporting during encryption/decryption: show the best trust
48 ; path to the recipient/signer?
49 ; completion on local id when signing (--list-secret-keys should know them)
50 ; algorithm preferences, possibly by destination user
51 ; (this is embedded in gpg)
52 ; extra options, possibly by destination user. Maybe for pgp5.0/pgp2.6 compat?
53 ; rfc2015 operation (MIME: application/pgp-signature, etc)
54 ; signature dates are currently reported with just the date. Find a time
55 ; formatting function and use the longtime in the VALIDSIG message.
57 ; mc-gpg-alternate-keyring seems dubious.. have two options, public/private?
59 ; using a shell introduces concerns about quoting and such. If the name of a
60 ; key used as a recipient or as a mc-gpg-user-id (a key to sign with) has a
61 ; double quote or ! or weird stuff, things could break.
63 ; encrypting to a nontrusted key is problematic: when not in --batch mode,
64 ; gpg warns the user and asks if they want to use the key anyway. In --batch
65 ; mode, it fails, even if we give --yes. Worse yet, if we encrypt to multiple
66 ; recipients, the untrusted ones get dropped withou flagging an error (stderr
67 ; does get a message, but it doesn't indicate which keys had a problem)
69 (defvar mc-gpg-user-id (user-login-name)
70 "*GPG ID of your default identity.")
71 (defvar mc-gpg-path "gpg" "*The GPG executable.")
72 (defvar mc-gpg-display-snarf-output nil
73 "*If t, pop up the GPG output window when snarfing keys.")
74 (defvar mc-gpg-always-fetch 'never
75 "*If t, always fetch missing keys. If 'never, never fetch. If nil,
77 (defvar mc-gpg-alternate-keyring nil
78 "*Public keyring to use instead of default.")
79 (defvar mc-gpg-comment
80 (format "Processed by Mailcrypt %s <http://mailcrypt.sourceforge.net/>"
82 "*Comment field to appear in ASCII armor output. If nil, let GPG use its
84 (defconst mc-gpg-msg-begin-line "^-----BEGIN PGP MESSAGE-----\r?$"
85 "Text for start of GPG message delimiter.")
86 (defconst mc-gpg-msg-end-line "^-----END PGP MESSAGE-----\r?$"
87 "Text for end of GPG message delimiter.")
88 (defconst mc-gpg-signed-begin-line "^-----BEGIN PGP SIGNED MESSAGE-----\r?$"
89 "Text for start of GPG signed messages.")
90 (defconst mc-gpg-signed-end-line "^-----END PGP SIGNATURE-----\r?$"
91 "Text for end of GPG signed messages.")
92 (defconst mc-gpg-key-begin-line "^-----BEGIN PGP PUBLIC KEY BLOCK-----\r?$"
93 "Text for start of GPG public key.")
94 (defconst mc-gpg-key-end-line "^-----END PGP PUBLIC KEY BLOCK-----\r?$"
95 "Text for end of GPG public key.")
96 (defvar mc-gpg-extra-args nil
97 "Extra arguments to pass to all invocations of gpg. Used during debugging to
98 set --homedir, to use special test keys instead of the developer's normal
100 (defvar mc-gpg-debug-buffer nil
101 "A buffer for debugging messages. If nil, no debugging messages are logged.
102 BEWARE! Sensitive data (including your passphrase) is put here. Set this with:
103 (setq mc-gpg-debug-buffer (get-buffer-create \"mc debug\"))")
105 ;; we use with-current-buffer for clarity. emacs19 doesn't have it. This
106 ;; code is cribbed from lazy-lock.el which does the same thing
108 ;; We use this for clarity and speed. Borrowed from a future Emacs.
109 (or (fboundp 'with-current-buffer)
110 (defmacro with-current-buffer (buffer &rest body)
111 "Execute the forms in BODY with BUFFER as the current buffer.
112 The value returned is the value of the last form in BODY."
113 (` (save-excursion (set-buffer (, buffer)) (,@ body)))))
116 (defun mc-gpg-debug-print (string)
117 (if (and (boundp 'mc-gpg-debug-buffer) mc-gpg-debug-buffer)
118 (print string mc-gpg-debug-buffer)))
120 ;; the insert parser will return '(t) and insert the whole of stdout if
121 ;; rc == 0, and will return '(nil rc stderr) if rc != 0
122 (defun mc-gpg-insert-parser (stdoutbuf stderrbuf statusbuf rc parserdata)
124 (format "(mc-gpg-generic-parser stdoutbuf=%s stderrbuf=%s rc=%s"
125 stdoutbuf stderrbuf rc))
128 (list nil nil rc (with-current-buffer stderrbuf (buffer-string))))
131 ;; the null parser returns rc and never inserts anything
132 (defun mc-gpg-null-parser (stdoutbuf stderrbuf statusbuf rc parserdata)
135 ; utility function (variant of mc-process-region):
136 ; take region in current buffer, send as stdin to a process
137 ; maybe send in a passphrase first
138 ; three buffers of output are collected: stdout, stderr, and --status-fd
140 ; parser is called with stdoutbuf as the current buffer as
141 ; (parser stdoutbuf stderrbuf statusbuf rc parserdata)
142 ; and is expected to return a list:
145 ; if REPLACEP is true, the original buffer's [beg..end] will be replaced by
146 ; the stdout data buffer's contents (all of it). Otherwise the original buffer
147 ; is left alone. RESULT (specifically (cdr parser-return-value)) is returned
148 ; by mc-gpg-process-region.
150 (defun mc-gpg-process-region (beg end passwd program args parser bufferdummy
151 &optional parserdata)
152 (let ((obuf (current-buffer))
153 (process-connection-type nil)
154 (shell-file-name "/bin/sh") ;; ??? force? need sh (not tcsh) for "2>"
157 stderr-tempfilename stderr-buf
158 status-tempfilename status-buf
159 proc rc status parser-result
161 (mc-gpg-debug-print (format
162 "(mc-gpg-process-region beg=%s end=%s passwd=%s program=%s args=%s parser=%s bufferdummy=%s)"
163 beg end passwd program args parser bufferdummy))
164 (setq stderr-tempfilename
165 (make-temp-name (expand-file-name "mailcrypt-gpg-stderr-"
167 (setq status-tempfilename
168 (make-temp-name (expand-file-name "mailcrypt-gpg-status-"
172 ;; get output places ready
173 (setq mybuf (get-buffer-create " *mailcrypt stdout temp"))
177 (buffer-disable-undo mybuf)
180 (setq args (append '("--passphrase-fd" "0") args)))
181 (setq args (append (list (concat "2>" stderr-tempfilename)) args))
182 (setq args (append (list (concat "3>" status-tempfilename)) args))
183 (setq args (append '("--status-fd" "3") args))
185 (if mc-gpg-extra-args
186 (setq args (append mc-gpg-extra-args args)))
188 (mc-gpg-debug-print (format "prog is %s, args are %s"
190 (mapconcat '(lambda (x)
195 (apply 'start-process-shell-command "*GPG*" mybuf
197 ;; send in passwd if necessary
200 (process-send-string proc (concat passwd "\n"))
201 (or mc-passwd-timeout (mc-deactivate-passwd t))))
202 ;; send in the region
203 (process-send-region proc beg end)
205 (process-send-eof proc)
206 ;; wait for it to finish
207 (while (eq 'run (process-status proc))
208 (accept-process-output proc 5))
209 ;; remember result codes
210 (setq status (process-status proc))
211 (setq rc (process-exit-status proc))
212 (mc-gpg-debug-print (format "prog finished, rc=%s" rc))
214 ;; Hack to force a status_notify() in Emacs 19.29
215 (delete-process proc)
217 ;; remove the annoying "yes your process has finished" message
219 (goto-char (point-max))
220 (if (re-search-backward "\nProcess \\*GPG.*\n\\'" nil t)
221 (delete-region (match-beginning 0) (match-end 0)))
222 (goto-char (point-min))
224 (while (search-forward "\r\n" nil t)
225 (replace-match "\n"))
227 ;; ponder process death: signal, not just rc!=0
228 (if (or (eq 'stop status) (eq 'signal status))
230 (error "%s exited abnormally: '%s'" program rc) ;;is rc a string?
234 (error "%s could not be found" program) ;; at least on my system
238 (setq stderr-buf (get-buffer-create " *mailcrypt stderr temp"))
239 (buffer-disable-undo stderr-buf)
240 (set-buffer stderr-buf)
242 (insert-file-contents stderr-tempfilename)
245 (setq status-buf (get-buffer-create " *mailcrypt status temp"))
246 (buffer-disable-undo status-buf)
247 (set-buffer status-buf)
249 (insert-file-contents status-tempfilename)
253 (setq parser-result (funcall parser
254 mybuf stderr-buf status-buf
256 (mc-gpg-debug-print (format " parser returned %s" parser-result))
258 ;; what did the parser tell us?
259 (if (car parser-result)
260 ;; yes, replace region
263 (delete-region beg end)
265 (insert-buffer-substring mybuf)
272 (if (and proc (eq 'run (process-status proc)))
273 ;; it is still running. kill it.
274 (interrupt-process proc))
276 (delete-file stderr-tempfilename)
277 (delete-file status-tempfilename)
278 ;; kill off temporary buffers unless we're debugging
279 (if (or (not (boundp 'mc-gpg-debug-buffer))
280 (not mc-gpg-debug-buffer))
282 (if (get-buffer " *mailcrypt stdout temp")
283 (kill-buffer " *mailcrypt stdout temp"))
284 (if (get-buffer " *mailcrypt stderr temp")
285 (kill-buffer " *mailcrypt stderr temp"))
286 (if (get-buffer " *mailcrypt status temp")
287 (kill-buffer " *mailcrypt status temp"))
292 ; this lookup is used to turn key identifiers into names suitable for
293 ; presentation to the user. When decrypting, the hex keyid to which the
294 ; incoming message is encrypted is looked up to ask the user for a passphrase
295 ; by name. When encrypting, the user's id (mc-gpg-user-id) is looked up to
296 ; ask for a passphrase, and if mc-gpg-encrypt-to-me is true, the user's id
297 ; is looked up to provide a full name to gpg. gpg is always given full names,
298 ; because the hex keyids it provides might not work for both signing and
299 ; encryption (split keys in gpg/pgp5)
301 ;31:warner@zs2-pc4% gpg --list-secret-keys --with-colons --no-greeting
302 ;/home/warner/.gnupg/secring.gpg
303 ;-------------------------------
304 ;sec::1024:17:1FE9CBFDC63B6750:1998-08-04:0:::Brian Warner (temporary GPG key) <warner@lothar.com>:
305 ;ssb::1024:20:C68E8DE9F759FBDE:1998-08-04:0:::
306 ;sec::768:17:16BD446D567E33CF:1998-08-04:0:::signature (sample signature key) <key@key>:
307 ;sec::768:16:D514CB72B37D9AF4:1998-08-04:0:::crypt (crypt) <crypt@crypt>:
308 ;sec::1024:17:4DBDD3258230A3E0:1998-08-04:0:::dummyy <d@d>:
309 ;ssb::1024:20:549B0E6CBBBB43D1:1998-08-04:0:::
311 ; we use the whole user id string (Brian..lothar.com>) as USER-ID, and the
312 ; long keyid 1FE9CBFDC63B6750 for KEY-ID
314 (defvar mc-gpg-key-cache nil
315 "Association list mapping GPG IDs to canonical \"keys\". A \"key\"
316 is a pair (USER-ID . KEY-ID) which identifies the canonical IDs of the
319 (defun mc-gpg-lookup-key (str &optional type)
320 ;; Look up the string STR in the user's secret key ring. Return a
321 ;; pair of strings (USER-ID . KEY-ID) which uniquely identifies the
322 ;; matching key, or nil if no key matches.
324 (if (string= str "***** CONVENTIONAL *****") nil
325 (let ((result (cdr-safe (assoc str mc-gpg-key-cache)))
327 "^\\(sec\\|pub\\):[^:]*:[^:]*:[^:]*:\\([^:]*\\):[^:]*:[^:]*:[^:]*:[^:]*:\\([^:]*\\):"
329 (obuf (current-buffer))
334 (setq buffer (generate-new-buffer " *mailcrypt temp"))
337 "--no-greeting" "--batch"
338 "--list-secret-keys" str
340 (if mc-gpg-alternate-keyring
341 (setq args (append (list "--keyring"
342 mc-gpg-alternate-keyring)
344 (if mc-gpg-extra-args
345 (setq args (append mc-gpg-extra-args args)))
347 (format "lookup: args are %s" args))
348 (let ((coding-system-for-read
349 (if (and (fboundp 'coding-system-p)
350 (coding-system-p 'utf-8))
352 (apply 'call-process mc-gpg-path nil buffer nil args))
354 (goto-char (point-min))
355 (if (re-search-forward key-regexp nil t)
358 (cons (buffer-substring-no-properties
359 (match-beginning 3) (match-end 3))
362 (buffer-substring-no-properties
363 (match-beginning 2) (match-end 2)))))
364 (setq mc-gpg-key-cache (cons (cons str result)
365 mc-gpg-key-cache)))))
366 ;(if buffer (kill-buffer buffer))
369 (error "No GPG secret key for %s" str))
372 ;gpg: no info to calculate a trust probability
373 ;gpg: no valid addressees
374 ;gpg: [stdin]: encryption failed: No such user id
376 (defun mc-gpg-encrypt-region (recipients start end &optional id sign)
377 (let ((process-environment process-environment)
378 (buffer (get-buffer-create mc-buffer-name))
379 (obuf (current-buffer))
380 action msg args key passwd result gpg-id)
381 (mc-gpg-debug-print (format
382 "(mc-gpg-encrypt-region recipients=%s start=%s end=%s id=%s sign=%s)"
383 recipients start end id sign))
386 "--batch" "--armor" "--textmode" "--always-trust"
387 (if recipients "--encrypt" "--store")
389 (setq action (if recipients "Encrypting" "Armoring"))
390 (setq msg (format "%s..." action)) ; May get overridden below
392 (setq args (append (list "--comment" (format "'%s'" mc-gpg-comment))
394 (if mc-gpg-alternate-keyring
395 (setq args (append (list "--keyring" mc-gpg-alternate-keyring) args)))
397 (if (and (not (eq mc-pgp-always-sign 'never))
398 (or mc-pgp-always-sign sign (y-or-n-p "Sign the message? ")))
400 (setq key (mc-gpg-lookup-key (or id mc-gpg-user-id) 'encrypt))
404 (format "GPG passphrase for %s (%s): " (car key) (cdr key))))
406 (append (list "--local-user" (cdr key)
410 (setq msg (format "%s+signing as %s ..." action (car key)))
412 ;; the --store is last in args. remove it. remove --textmode too
413 (setq args (nreverse (cddr (nreverse args)))))
417 ; if we're supposed to encrypt for the user too, we need their key
418 ;; FIXME: we only need their public key, not the secret one. Some users
419 ;; (the author included) keep their secret keys offline unless needed
420 ;; (but the public ones are still available).. the --list-secret-keys
421 ;; done by mc-gpg-lookup-key will fail in this situation. Change
422 ;; mc-gpg-lookup-key to have a way to look for public keys too.
423 (if (and recipients mc-encrypt-for-me)
424 (setq recipients (cons (cdr (or key
425 (setq key (mc-gpg-lookup-key
426 mc-gpg-user-id 'encrypt)))
429 ; push(@args, map {qq<-r "$_">} @recipients) if @recipients; # roughly
431 (setq args (append (apply 'append
434 (concat "\"" x "\"")))
439 (setq result (mc-gpg-process-region start end passwd mc-gpg-path args
440 'mc-gpg-insert-parser buffer))
441 (if (not (car result))
442 (error "%s failed: %s" msg (nth 2 result)))
448 ; GPG DECRYPT BEHAVIOR: gnupg-0.9.9 only
449 ; (all status messages are prefixed by "[GNUPG:] "
451 ; signed (not encrypted) by a known key [S.s1v]:
452 ; rc == 0, stdout has message
454 ; SIG_ID <sigid> <date> <longtime>
455 ; GOODSIG <longkeyid> <username>
456 ; VALIDSIG <keyfingerprint> <date> <longtime>
459 ; signed (not encrypted) by unknown key [S.s4]:
460 ; rc == 2, stdout has message
462 ; ERRSIG <longkeyid> <pubkeyalgo> <hashalgo> <sigclass> <longtime> <rc>
463 ; NO_PUBKEY <longkeyid>
465 ; encrypted to a private key we don't have [E.e3]:
467 ; stderr: gpg: decryption failed: secret key not available
469 ; ENC_TO <longkeyid> <keytype> <keylength==0>
470 ; NO_SECKEY <longkeyid>
473 ; encrypted to us, our key has no passphrase
475 ; stderr: gpg: NOTE: secret key foo is NOT protected
477 ; ENC_TO <longkeyid> <keytype> <keylen==0>
481 ; encrypted to us, but we didn't give a passphrase [E.e1r, no pw]:
483 ; stderr: gpg: fatal: Can't query password in batchmode
485 ; ENC_TO <longkeyid> <keytype> <keylength==0>
486 ; NEED_PASSPHRASE <longkeyid> <otherlongkeyid> <keytype> <keylen==0>
487 ; MISSSING_PASSPHRASE
488 ; BAD_PASSPHRASE <longkeyid>
490 ; (N.B.: gpg cannot tell tell the difference between no passphrase and an
493 ; encrypted to us *and someone else*, no passphrase [E.e3re1r, no pw]:
495 ; stderr: gpg: fatal: Can't query password in batchmode
497 ; ENC_TO <longkeyid1> <keytype> <keylength==0>
498 ; NEED_PASSPHRASE <longkeyid1> <otherlongkeyid> <keytype> <keylen==0>
499 ; MISSSING_PASSPHRASE
500 ; BAD_PASSPHRASE <longkeyid1>
501 ; ENC_TO <longkeyid2> .. ..
502 ; NO_SECKEY <longkeyid2>
505 ; encrypted to us, but we used the wrong passphrase [E.e1r, bad pw]:
507 ; stderr: gpg: public key decryption failed: [Bb]ad passphrase
509 ; ENC_TO <longkeyid> <keytype> <keylength==0>
510 ; NEED_PASSPHRASE <longkeyid> <otherlongkeyid> <keytype> <keylen==0>
511 ; BAD_PASSPHRASE <longkeyid>
514 ; encrypted to us, good passphrase [E.e1r, good pw]:
515 ; rc == 0, stdout has message
517 ; ENC_TO <longkeyid> <keytype> <keylength==0>
518 ; NEED_PASSPHRASE <longkeyid> <otherlongkeyid> <keytype> <keylen==0>
522 ; encrypted to us, good passphrase, signed by trusted/untrusted party
523 ; [ES.e1r.s1v, good ps]:
524 ; rc == 0, stdout has message
525 ; stderr: gpg: Signature made <date> using DSA key ID <pubkeyid>
526 ; stderr: gpg: Good signature from "<keyname>"
528 ; ENC_TO <longkeyid> <keytype> <keylength==0>
529 ; NEED_PASSPHRASE <longkeyid> <otherlongkeyid> <keytype> <keylen==0>
531 ; SIG_ID <sigid> <date> <longtime>
532 ; GOODSIG <longkeyid> <username>
533 ; VALIDSIG <keyfingerprint> <date> <longtime>
534 ; TRUST_(UNDEFINED|NEVER|MARGINAL|FULLY|ULTIMATE)
537 ; encrypted to us, good passphrase, signed by unknown party [ES.e1r.s4]:
538 ; rc == 2, stdout has message
539 ; stderr: gpg: Signature made <date> using DSA key ID <pubkeyid>
540 ; stderr: gpg: Can't check signature: [Pp]ublic key not found
542 ; ENC_TO <longkeyid> <keytype> <keylength==0>
543 ; NEED_PASSPHRASE <longkeyid> <otherlongkeyid> <keytype> <keylen==0>
545 ; ERRSIG <longkeyid> <pubkeyalgo> <hashalgo> <sigclass> <longtime> <rc>
546 ; rc: 4 is unknown algorithm, 9 is missing public key
547 ; NO_PUBKEY <longkeyid>
550 ; symmetrically encrypted, we didn't give a passphrase
551 ; rc == 2, stderr: gpg: fatal: Can't query password in batchmode
553 ; NEED_PASSPHRASE_SYM <cipheralgo> <s2kmode> <s2khash>
557 ; symmetrically encrypted, we gave the wrong passphrase
558 ; rc == 2, stderr: gpg: decryption failed: [Bb]ad key
560 ; NEED_PASSPHRASE_SYM <cipheralgo> <s2kmode> <s2khash>
563 ; symmetrically encrypted, good passphrase
564 ; rc == 0, stdout: message
566 ; NEED_PASSPHRASE_SYM <cipheralgo> <s2kmode> <s2khash>
570 ; rc == 0, stdout: message
574 ; rc == 2, stderr: gpg: CRC error; stuff - stuff
576 ; ( to test: multiple recipients, keys without passphrases)
579 ;; this parser's return convention:
581 ;; replacep ; consumed by process-region: decrypt was successful
582 ;;0 have-secret-key ; t: we are a recipient (TODO: stealth),
583 ;; 'symmetric : need passphrase
584 ;; 'signed : signed not encrypted
585 ;; nil: not a recipient
586 ;;1 passphrase-ok ; t was good, nil was bad, keyid: need pw for keyid
589 ;; keyid-hex : don't have signature key
590 ;; '(keyid-string t trust date) : good signature on date with trust
591 ;; '(keyid-string nil trust date) : bad signature on date with trust
594 ; todo: stealth ("--throw-keyid")?
595 ; when there is a signature that we can't check because of a bad algo
596 ; then we pretend there wasn't a signature. extend the return convention
597 ; to signal this case.
598 ; when there is a signature that we can't check because we don't
599 ; currently have a key, and if we successfully fetch that key in
600 ; mc-gpg-decrypt-region, how do we restart the operation?
603 ;; *not addressed to us (nil nil nil)
604 ;; *just armored (same as good symmetric) ('symmetric t nil)
605 ;; conventionally encrypted
606 ;; *didn't give passphrase ('symmetric "***** CONVENTIONAL *****" nil)
607 ;; did give passphrase
608 ;; *bad passphrase ('symmetric nil nil)
609 ;; *good passphrase ('symmetric t nil)
610 ;; signed (not clearsigned), not encrypted
611 ;; *don't have key ('signed t keyid)
613 ;; *good sig ('signed t (t keyid-string trust date))
614 ;; *bad sig ('signed t (nil keyid-string trust date))
616 ;; *didn't give passphrase (t keyid nil)
618 ;; *bad passphrase (t nil nil)
621 ;; *no signature (t t nil)
623 ;; *don't have key (offer to fetch) (t t keyid)
625 ;; *good sig (t t (t keyid-string date trust))
626 ;; *bad sig (t t (nil keyid-string date trust))
628 ;; a subfunction to extract the signature info. Used in both decrypt-parser
629 ;; and verify-parser. Call with statusbuf. Returns
630 ;; '(sigtype sigid sigdate sigtrust)
632 (defun mc-gpg-sigstatus-parser ()
633 (let (sigtype sigid sigdate sigtrust)
635 ;; sigtype: GOOD, BAD, ERR
636 ;; sigid: who made the signature? (a name if possible, else hex keyid)
637 ;; sigdate: date string of when the sig was made
638 (goto-char (point-min))
639 (if (re-search-forward "^\\[GNUPG:\\] +\\(GOOD\\|BAD\\|ERR\\)SIG\\b"
642 (setq sigtype (match-string 1))
643 (goto-char (point-min))
644 (if (and (or (string= sigtype "GOOD") (string= sigtype "BAD"))
646 "^\\[GNUPG:\\] +\\(GOOD\\|BAD\\)SIG +\\(\\S +\\) +\\(.*\\)$" nil t))
647 ;; match-string 2 is the hex keyid of the signator.
649 (setq sigid (match-string 3)))
652 ;; match-string #1 is the hex keyid, #2 is the algorithm ID
653 ;; (17: DSA, 1,3: RSA, 20: Elgamal)
654 ;; #3: hashalgo, #4: sigclass, #5: longtime, #6: rc
655 ;; (rc==4 for unknown algo, 9 for missing public key)
656 ;; we only set sigtype if:
657 ;; (#1 is present), and
658 ;; ((#6 is missing) or (#6 == 9))
659 ;; the idea being to not fetch a key if we aren't going to be able
660 ;; to use the algorithm it wants
661 (goto-char (point-min))
662 (if (and (string= sigtype "ERR")
664 "^\\[GNUPG:\\] +ERRSIG +\\(\\S +\\)" nil t))
665 (let (errsig-rc (sigid-temp (match-string 1)))
666 (goto-char (point-min))
667 (if (re-search-forward
668 "^\\[GNUPG:\\] +ERRSIG +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\)" nil t)
669 (setq errsig-rc (match-string 6)))
672 (string= errsig-rc "9"))
673 (setq sigid sigid-temp))
677 ;; VALIDSIG should be present, with <keyfingerprint> <date> <time>
678 (goto-char (point-min))
679 (if (and (string= sigtype "GOOD")
681 "^\\[GNUPG:\\] +SIG_ID +\\(\\S +\\) +\\(\\S +\\)\\b"
683 (setq sigdate (match-string 2))
684 ;; in gpg >= 0.9.7, a third field is a longtime value (seconds
688 ;; sigtrust: how trusted is the signing key?
689 (goto-char (point-min))
690 (if (re-search-forward "^\\[GNUPG:\\] +\\(TRUST_\\S +\\)$" nil t)
691 (setq sigtrust (match-string 1)))
694 (list sigtype sigid sigdate sigtrust))
698 ; this parser's job is to find the decrypted data if any is available. The
699 ; code in -decrypt-region will worry about reporting other status information
700 ; like signatures. PARSERDATA is non-nil if a passphrase was given to GPG.
702 (defun mc-gpg-decrypt-parser (stdoutbuf stderrbuf statusbuf rc parserdata)
705 decryptstatus ; DECRYPTION_(OKAY|FAILED)
706 no-seckey ; NO_SECKEY
707 keyid ; NEED_PASSPHRASE <keyid>
708 missing-passphrase ; MISSING_PASSPHRASE
709 symmetric ; NEED_PASSPHRASE_SYM
710 badpass ; BAD_PASSPHRASE
711 sigtype ; GOODSIG, BADSIG, ERRSIG
712 sigid ;; GOODSIG <keyid> (note: not SIG_ID!),
713 ;;; or ERRSIG <keyid> if ERRSIG-rc is 9 for missing pubkey
714 sigdate ; VALIDSIG .. <date>
715 sigtrust ; TRUST_(UNDEFINED|NEVER|MARGINAL|FULLY|ULTIMATE)
717 ;; this code is split into two pieces. The first scans statusbuf
718 ;; (and stderr if absolutely necessary) for keywords, setting the
719 ;; local variables to describe what happened during our decryption attempt.
720 ;; We don't try too hard to interpret the results yet.
722 ;; the second part (the big cond statement below) interprets those vars
723 ;; to decide what to report to the caller
725 (set-buffer statusbuf)
727 ;; decryptstatus: no decryption took place, one was ok, or one failed
728 (goto-char (point-min))
729 (if (re-search-forward
730 "^\\[GNUPG:\\] +DECRYPTION_\\(OKAY\\|FAILED\\)\\b"
732 (setq decryptstatus (match-string 1)))
734 ;; no-seckey: set if we saw a NO_SECKEY message.
735 (goto-char (point-min))
736 (if (re-search-forward
737 "^\\[GNUPG:\\] +NO_SECKEY\\b"
741 ;; keyid: the message is encrypted to one of our private keys and we
742 ;; need a passphrase from the user. which one?
743 (goto-char (point-min))
744 (if (re-search-forward "^\\[GNUPG:\\] +NEED_PASSPHRASE +\\(\\S +\\)"
746 (setq keyid (concat "0x" (match-string 1))))
748 ;; missing-passphrase: set if we saw MISSING_PASSPHRASE
749 (goto-char (point-min))
750 (if (re-search-forward "^\\[GNUPG:\\] +MISSING_PASSPHRASE\\b"
752 (setq missing-passphrase t))
754 ;; symmetric: Set if the message is symmetrically encrypted.
755 (goto-char (point-min))
756 (if (re-search-forward
757 "^\\[GNUPG:\\] +NEED_PASSPHRASE_SYM\\b"
761 ;; badpass: GPG did not get a good passphrase. Either we didn't give one
762 ;; or we gave the wrong one.
763 (goto-char (point-min))
764 (if (re-search-forward "^\\[GNUPG:\\] +BAD_PASSPHRASE\\b"
768 (let ((sigstuff (mc-gpg-sigstatus-parser)))
769 (setq sigtype (nth 0 sigstuff))
770 (setq sigid (nth 1 sigstuff))
771 (setq sigdate (nth 2 sigstuff))
772 (setq sigtrust (nth 3 sigstuff))
775 ;; begin second piece: stare at those variables and decide what happened.
776 ;; refer to the "cases:" comment above for what we look for.
780 "decrypt-parser: decryptstatus=%s no-seckey=%s keyid=%s missing-passphrase=%s symmetric=%s badpass=%s sigtype=%s sigid=%s sigdate=%s sigtrust=%s rc=%s"
781 decryptstatus no-seckey keyid missing-passphrase symmetric badpass sigtype sigid sigdate sigtrust rc))
785 ((and (not decryptstatus) (not (or keyid symmetric)))
786 ;; either corrupt, armored-only, signed-only
787 ;; or we're using an old gpg and no passphrase was requested:
788 ;; either corrupt, armored-only, signed-only, or not for us.
791 ;; signed-only. extract info
793 ((string= sigtype "GOOD") ;; good signature
794 (list t 'signed t (list t sigid sigtrust sigdate)))
795 ((string= sigtype "BAD") ;; bad signature
796 (list t 'signed t (list nil sigid sigtrust sigdate)))
797 ((string= sigtype "ERR") ;; couldn't check: why?
799 ;; didn't have the key, we can fetch it
800 (list t 'signed t sigid)
801 ;; can't use it. pretend it wasn't signed.
803 (t ;; sigtype is bogus
804 (error "sigtype was bogus. Shouldn't happen."))
806 ((not (= rc 0)) ;; corrupt
807 (error "The message was corrupt."))
809 (list t 'symmetric t nil))
813 (string= decryptstatus "FAILED")
814 ;; couldn't decrypt: not to us, need pw, bad pw
815 (and (not decryptstatus)
818 (not (string= sigtype "ERR")))
819 ;; or old gpg and we could have decrypted it (a passphrase was
820 ;; requested), but the decrypt went bad (rc!=0 but not due to ERRSIG)
823 ((and (not symmetric) (not keyid))
824 ;; didn't ask for a passphrase, ergo it isn't for us
825 (list nil nil nil nil))
826 ((or missing-passphrase (not parserdata))
827 ;; we didn't give a passphrase, need pubkey or symmetric
829 (list nil 'symmetric "***** CONVENTIONAL *****" nil)
830 (list nil t keyid nil)))
831 (symmetric ;; symmetric fails without a BAD_PASSPHRASE
832 (list nil 'symmetric nil nil))
833 ((or badpass parserdata)
834 ;; probably pubkey, we gave the wrong passphrase
835 (list nil t nil nil))
836 (t ;; shouldn't happen, error out
837 (error "decryption failed, but I don't know why. Shouldn't happen."))
841 (string= decryptstatus "OKAY")
842 ;; decrypted okay, check for signature
843 (and (not decryptstatus)
846 (string= sigtype "ERR"))
847 ;; or old gpg and sigcheck went bad (rc!=0 due to ERRSIG)
848 (and (not decryptstatus)
851 ;; or old gpg, passphrase was requested, no errors reported
854 (sigtype ;; there was a signature, extract the info (never sym here)
856 ((string= sigtype "GOOD") ;; good signature
857 (list t t t (list t sigid sigtrust sigdate)))
858 ((string= sigtype "BAD") ;; bad signature
859 (list t t t (list nil sigid sigtrust sigdate)))
860 ((string= sigtype "ERR") ;; couldn't check: why?
862 ;; didn't have the key. we can fetch it.
864 ;; no keyid, or we can't use it. pretend there wasn't a sig.
866 (t ;; sigtype is bogus
867 (error "sigtype was bogus. Shouldn't happen."))
869 (t ;; there wasn't a signature
871 (list t 'symmetric t nil)
875 (t ;; decryptstatus was bogus. error out.
876 (error "decryptstatus was bogus '%s'. Shouldn't happen." decryptstatus))
884 ;; message about who made the signature. This is a bit wide.. the date can
885 ;; easily run off the echo area. Consider replacing 'Good signature' with
886 ;; 'good sig', but keep it consistent with everything else. This function is
887 ;; used by both the decrypt section and the verify section.
888 ;; todo: should the keyid be put in here? If the user reads the trustvalue,
889 ;; and if they have a trust path, then they can trust the name.
890 (defun mc-gpg-format-sigline (goodp sigid sigtrust sigdate)
892 (format "Good signature from '%s' %s made %s"
893 sigid sigtrust sigdate)
894 (format "BAD SIGNATURE claiming to be from '%s'" sigid)
897 ;; decrypt-region is first called without ID. This means we'll try to decrypt
898 ;; without a passphrase, almost guaranteed to fail, but it will tell us which
899 ;; key is necessary. We then call decrypt-region again, this time with ID
900 ;; set. This second time will lookup ID and ask the user for the passphrase.
902 (defun mc-gpg-decrypt-region (start end &optional id)
903 ;; returns a pair (SUCCEEDED . VERIFIED) where SUCCEEDED is t if
904 ;; the decryption succeeded and verified is t if there was a valid signature
905 (let ((process-environment process-environment)
906 (buffer (get-buffer-create mc-buffer-name))
907 (obuf (current-buffer))
908 args key new-key passwd result gpg-id)
909 (mc-gpg-debug-print (format "(mc-gpg-decrypt-region start=%s end=%s id=%s)"
913 ;; second time through, now we know who the message is for.
914 ;; id is either a hex keyid of the (first?) secret key that is in
915 ;; the message's recipient list, or "**..CONVENTIONAL.."
917 (setq key (mc-gpg-lookup-key id 'encrypt))
918 ;; key is nil if CONVENTIONAL, (string . hexid) otherwise
921 (mc-activate-passwd (cdr key)
923 "GPG passphrase for %s (%s): "
924 (car key) (cdr key)))
926 id "GPG passphrase for conventional decryption: ")))
927 (if (string= passwd "")
929 (mc-deactivate-passwd t)
930 (error "Empty passphrases are bad, mmkay?")))
931 ;; in particular, they cause an infinite loop. If the key doesn't
932 ;; have a passphrase, the decryption should have worked the first
935 (setq args '("--batch"))
936 (if mc-gpg-alternate-keyring
937 (setq args (append args (list "--keyring" mc-gpg-alternate-keyring))))
938 (setq args (append args '("--decrypt"))) ; this wants to be last
939 (message "Decrypting...")
940 ;; pass ID as the parserdata. This will be non-nil if a passphrase was
941 ;; given (i.e. 2nd pass), which affects decrypt status parsing
943 (mc-gpg-process-region
944 start end passwd mc-gpg-path args 'mc-gpg-decrypt-parser buffer id))
945 ;(message "Decrypting... Done.")
946 ;; result: '(HAVE-SECRET-KEY PASSPHRASE-OK SIG)
947 ;; SIG: nil, sigkeyid, or '(KEYID GOODP TRUSTLEVEL DATESTRING)
949 ((not (nth 0 result)) ;; we were not a recipient
950 (error "This message is not addressed to you"))
951 ((not (nth 1 result)) ;; passphrase-ok is nil: bad passphrase
952 (mc-deactivate-passwd t)
953 (error "That passphrase was wrong"))
954 ((not (equal (nth 1 result) t)) ;; passphrase-ok is keyid: need passphrase
955 ;; get passphrase for (nth 1 result), try again
956 (mc-gpg-decrypt-region start end (nth 1 result))
958 ;; passphrase was ok, were able to decrypt
959 ((nth 2 result) ;; there was a signature
960 (let ((sig (nth 2 result)))
962 ((atom sig) ;; don't have the signature key
964 ;; offer to fetch the key, then what? run again? must we undo 1st?
965 (mc-message-sigstatus
966 (format "cannot check signature from keyid %s" sig))
967 (if (and (not (eq mc-gpg-always-fetch 'never))
968 (or mc-gpg-always-fetch
970 (format "Key %s not found; attempt to fetch? " sig)))
971 (mc-gpg-fetch-key (cons nil sig)))
975 (mc-gpg-decrypt-region start end id))
978 ((nth 0 sig) ;; good signature
980 (mc-message-sigstatus (mc-gpg-format-sigline
981 t (nth 1 sig) (nth 2 sig) (nth 3 sig)))
986 (mc-message-sigstatus (mc-gpg-format-sigline
987 nil (nth 1 sig) (nth 2 sig) (nth 3 sig))
988 t ; get their attention
994 (message "Decrypting... Done.")
999 (defun mc-gpg-sign-region (start end &optional id unclear)
1000 (let ((process-environment process-environment)
1001 (buffer (get-buffer-create mc-buffer-name))
1002 passwd args key result)
1003 (setq key (mc-gpg-lookup-key (or id mc-gpg-user-id) 'sign))
1007 (format "GPG passphrase for %s (%s): " (car key) (cdr key))))
1011 "--local-user" (cdr key)
1012 (if unclear "--sign" "--clearsign")
1015 (setq args (append (list "--comment" (format "'%s'" mc-gpg-comment))
1017 (if mc-gpg-extra-args
1018 (setq args (append mc-gpg-extra-args args)))
1019 (message "Signing as %s ..." (car key))
1020 (setq result (mc-gpg-process-region start end passwd mc-gpg-path args
1021 'mc-gpg-insert-parser buffer))
1023 (message "Signing as %s ... Done." (car key))
1025 (mc-deactivate-passwd t)
1026 (error "Signature failed: %s" (nth 2 result))
1032 ; GPG VERIFY BEHAVIOR: gnupg-0.9.9 only
1033 ; (all status messages are prefixed by "[GNUPG:] "
1034 ; (filenames in [] are my parts of my testsuite)
1036 ; corrupted sig (armor is corrupt) [CS.s1bad]:
1038 ; stderr: gpg: CRC error; stuff - stuff
1039 ; gpg: packet(1) with unknown version
1041 ; GOOD sig from a known key [CS.s1v,CS.s2v,CS.s3v]
1044 ; SIG_ID <sigid> <date> <longtime>
1045 ; GOODSIG <longkeyid> <username>
1046 ; VALIDSIG <keyfingerprint> <date> <longtime>
1047 ; TRUST_(UNDEFINED|NEVER|MARGINAL|FULLY|ULTIMATE)
1049 ; BAD sig from a known key [CS.s1f]:
1051 ; status: BADSIG <longkeyid> <username>
1053 ; unknown key [CS.s4]:
1056 ; ERRSIG <longkeyid> <pubkeyalgo> <hashalgo> <sigclass> <longtime> <rc==9>
1057 ; NO_PUBKEY <longkeyid>
1059 ;; so no status messages mean armor corruption
1061 ;; return convention for mc-gpg-verify-parser:
1062 ;; (same as sig section of decrypt parser)
1063 ;; sigid : signed by an unknown key, need this key to verify
1064 ;; '(t sigid sigtrust sigdate): good sig from sigid
1065 ;; '(nil sigid sigtrust sigdate): forged sig "from" sigid
1066 ;; (actual return includes a leading nil because the verify-parser should
1067 ;; never replace the region with stdout)
1069 (defun mc-gpg-verify-parser (stdoutbuf stderrbuf statusbuf rc parserdata)
1070 (let (sigtype sigid sigdate sigtrust)
1071 ;; parse FOOSIG with the same code as decrypt-parser
1072 (set-buffer statusbuf)
1074 (let ((sigstuff (mc-gpg-sigstatus-parser)))
1075 (setq sigtype (nth 0 sigstuff))
1076 (setq sigid (nth 1 sigstuff))
1077 (setq sigdate (nth 2 sigstuff))
1078 (setq sigtrust (nth 3 sigstuff))
1083 "decrypt-parser: sigtype=%s sigid=%s sigdate=%s sigtrust=%s"
1084 sigtype sigid sigdate sigtrust))
1086 (if (and (not (= rc 0))
1088 (error "The message was corrupt."))
1091 ((string= sigtype "ERR")
1093 ((string= sigtype "GOOD")
1094 (list nil (list t sigid sigtrust sigdate))) ;; good sig
1096 (list nil (list nil sigid sigtrust sigdate))))
1100 ; check a signature, print a message about its validity. Returns t if the
1101 ; sig was valid, nil otherwise
1103 (defun mc-gpg-verify-region (start end &optional no-fetch)
1104 (let ((buffer (get-buffer-create mc-buffer-name))
1105 (obuf (current-buffer))
1107 (setq args '("--batch" "--verify"))
1108 (if mc-gpg-alternate-keyring
1109 (setq args (append "--keyring" mc-gpg-alternate-keyring args)))
1110 (message "Verifying...")
1111 (setq result (mc-gpg-process-region
1112 start end nil mc-gpg-path args 'mc-gpg-verify-parser buffer))
1113 (mc-gpg-debug-print (format "process-region returned %s" result))
1114 (setq result (car result))
1122 (not (eq mc-gpg-always-fetch 'never))
1123 (or mc-gpg-always-fetch
1125 (format "Key %s not found; attempt to fetch? " result)))
1126 (mc-gpg-fetch-key (cons nil result))
1128 (mc-gpg-verify-region start end t)
1129 (error "Can't check signature: Public key %s not found" result)))
1134 (message (mc-gpg-format-sigline
1135 t (nth 1 result) (nth 2 result) (nth 3 result)))
1142 (message (mc-gpg-format-sigline
1143 nil (nth 1 result) (nth 2 result) (nth 3 result)))
1148 (defun mc-gpg-insert-public-key (&optional id)
1149 (let ((buffer (get-buffer-create mc-buffer-name))
1151 (setq id (or id mc-gpg-user-id))
1152 (setq args (list "--export" "--armor" "--batch" (concat "\"" id "\"")))
1154 (setq args (append (list "--comment" (format "'%s'" mc-gpg-comment))
1156 (if mc-gpg-alternate-keyring
1157 (setq args (append (list "--keyring" mc-gpg-alternate-keyring) args)))
1159 (setq result (mc-gpg-process-region (point) (point) nil mc-gpg-path
1160 args 'mc-gpg-insert-parser buffer))
1162 (message (format "Key for user ID: %s" id))
1163 (message "failed: %s" (nth 2 result)))
1167 ;; GPG IMPORT BEHAVIOR: gnupg-0.9.9 only
1170 ;; IMPORT_RES (12 fields)
1171 ;; 1 <count> : number of keys seen
1172 ;; 2 <no_user_id> : the number of keys without valid userids, including
1173 ;; keys that weren't self-signed
1174 ;; 3 <imported> : new public keys
1175 ;; 4 <imported_rsa> : new RSA public keys (included in <imported>)
1176 ;; 5 <unchanged> : old public keys
1181 ;; 10 <sec_read> : number of secret keys seen
1182 ;; 11 <sec_imported> : new secret keys
1183 ;; 12 <sec_dups> : old secret keys
1185 ;; the first three are for public keys, the last three are for secret keys.
1186 ;; add them together, I guess. It's unlikely that anyone will be importing
1187 ;; armored secret keys via email, but if they do it will be reported as if
1188 ;; it were a public key.
1190 ;; return convention:
1191 ;; error with stderr if rc != 0
1192 ;; '(count bad new old changed secretp)
1194 (defun mc-gpg-snarf-parser (stdoutbuf stderrbuf statusbuf rc parserdata)
1196 (let (count bad new old changed secretp)
1197 (set-buffer statusbuf)
1198 (goto-char (point-min))
1199 (if (re-search-forward
1200 "^\\[GNUPG:\\] +IMPORT_RES +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\) +\\(\\S +\\)"
1203 (setq count (string-to-number (match-string 1)))
1204 (setq bad (string-to-number (match-string 2)))
1205 (setq new (+ (string-to-number (match-string 3))
1206 (string-to-number (match-string 11))))
1207 (setq old (+ (string-to-number (match-string 5))
1208 (string-to-number (match-string 12))))
1209 (setq changed (- count bad new old))
1210 (setq secretp (not (string= (match-string 10) "0")))
1211 (list nil count bad new old changed secretp))
1212 (error "No key import status: your GnuPG is too old."))
1214 (error (with-current-buffer stderrbuf (buffer-string))))
1217 (defun mc-gpg-snarf-keys (start end)
1218 ;; Returns number of keys found.
1219 (let ((buffer (get-buffer-create mc-buffer-name))
1221 (setq args '("--import" "--batch"))
1222 (if mc-gpg-alternate-keyring
1223 (setq args (append args (list "--keyring" mc-gpg-alternate-keyring))))
1224 (message "Snarfing...")
1225 (setq results (mc-gpg-process-region start end nil mc-gpg-path args
1226 'mc-gpg-snarf-parser buffer))
1227 ;; don't have to update trustdb: gpg does it automatically (although it
1228 ;; might take a few seconds if a lot of keys or signatures have been
1231 ;; Is there any point to displaying this message? mc-snarf-keys will
1232 ;; display a simple "%d new keys found" message right after we return.
1233 ;; Well, print it anyway, if the user looks in the *Messages* buffer
1234 ;; they'll see more.
1235 (setq msg (format "%d keys seen" (nth 0 results)))
1236 (if (not (zerop (nth 1 results)))
1237 (setq msg (concat msg (format ", %d bad" (nth 1 results)))))
1238 (if (not (zerop (nth 2 results)))
1239 (setq msg (concat msg (format ", %d new" (nth 2 results)))))
1240 (if (not (zerop (nth 3 results)))
1241 (setq msg (concat msg (format ", %d old" (nth 3 results)))))
1242 (if (not (zerop (nth 4 results)))
1243 (setq msg (concat msg (format ", %d changed" (nth 4 results)))))
1245 (setq msg (concat msg ", SECRET KEYS IMPORTED")))
1251 (defun mc-scheme-gpg ()
1253 (cons 'encryption-func 'mc-gpg-encrypt-region)
1254 (cons 'decryption-func 'mc-gpg-decrypt-region)
1255 (cons 'signing-func 'mc-gpg-sign-region)
1256 (cons 'verification-func 'mc-gpg-verify-region)
1257 (cons 'key-insertion-func 'mc-gpg-insert-public-key)
1258 (cons 'snarf-func 'mc-gpg-snarf-keys)
1259 (cons 'msg-begin-line mc-gpg-msg-begin-line)
1260 (cons 'msg-end-line mc-gpg-msg-end-line)
1261 (cons 'signed-begin-line mc-gpg-signed-begin-line)
1262 (cons 'signed-end-line mc-gpg-signed-end-line)
1263 (cons 'key-begin-line mc-gpg-key-begin-line)
1264 (cons 'key-end-line mc-gpg-key-end-line)
1265 (cons 'user-id mc-gpg-user-id)))
1269 (defvar mc-gpg-always-fetch 'never
1270 "*If t, always attempt to fetch missing keys, or never fetch if
1273 (defun mc-gpg-fetch-key (&optional id)
1274 "Attempt to fetch a key for addition to GPG keyring. Interactively,
1275 prompt for string matching key to fetch.
1277 This function is not yet implemented. The GPG documentation suggests a simple
1278 keyserver protocol, but as far as I know it has not yet been implemented
1281 (error "Key fetching not yet implemented"))