2 "EasyPG, yet another GnuPG interface.")
4 (defcustom epg-gpg-program "gpg"
5 "The `gpg' executable."
9 (defvar epg-user-id nil
10 "GnuPG ID of your default identity.")
12 (defvar epg-user-id-alist nil
13 "An alist mapping from key ID to user ID.")
15 (defvar epg-read-point nil)
16 (defvar epg-pending-status-list nil)
17 (defvar epg-key-id nil)
18 (defvar epg-context nil)
19 (defvar epg-debug nil)
21 (defvar epg-colons-pub-spec
23 (length "[0-9]+" 0 string-to-number)
24 (algorithm "[0-9]+" 0 string-to-number)
26 (creation-date "[0-9]+")
27 (expiration-date "[0-9]+")
32 (capability "[escaESCA]*"))
33 "The schema of keylisting output whose type is \"pub\".
34 This is used by `epg-list-keys'.")
36 (defvar epg-colons-sec-spec
38 (length "[0-9]+" 0 string-to-number)
39 (algorithm "[0-9]+" 0 string-to-number)
41 (creation-date "[0-9]+")
42 (expiration-date "[0-9]+")
45 "The schema of keylisting output whose type is \"sec\".
46 This is used by `epg-list-keys'.")
48 (defvar epg-colons-uid-spec
53 (creation-date "[0-9]+")
54 (expiration-date "[0-9]+")
58 "The schema of keylisting output whose type is \"uid\".
59 This is used by `epg-list-keys'.")
61 (defun epg-make-context (&optional protocol armor textmode include-certs
62 process output-file result)
63 "Return a context object."
64 (vector protocol armor textmode include-certs
65 (list #'epg-passphrase-callback)
66 (list #'epg-progress-callback)
67 nil process output-file result))
69 (defun epg-context-protocol (context)
70 "Return the protocol used within the context."
73 (defun epg-context-armor (context)
74 "Return t if the output shouled be ASCII armored in the CONTEXT context."
77 (defun epg-context-textmode (context)
78 "Return t if canonical text mode should be used in the CONTEXT context."
81 (defun epg-context-include-certs (context)
82 "Return how many certificates should be included in an S/MIME signed
86 (defun epg-context-passphrase-callback (context)
87 "Return the function used to query passphrase."
90 (defun epg-context-progress-callback (context)
91 "Return the function which handles progress update."
94 (defun epg-context-signers (context)
95 "Return the list of key-id for singning."
98 (defun epg-context-process (context)
99 "Return the process object of `epg-gpg-program'.
100 This function is for internal use only."
103 (defun epg-context-output-file (context)
104 "Return the output file of `epg-gpg-program'.
105 This function is for internal use only."
108 (defun epg-context-result (context name)
109 "Return the result of the previous cryptographic operation."
110 (cdr (assq name (aref context 9))))
112 (defun epg-context-set-protocol (context protocol)
113 "Set the protocol used within the context."
114 (aset context 0 protocol))
116 (defun epg-context-set-armor (context armor)
117 "Specify if the output shouled be ASCII armored in the CONTEXT context."
118 (aset context 1 armor))
120 (defun epg-context-set-textmode (context textmode)
121 "Specify if canonical text mode should be used in the CONTEXT context."
122 (aset context 2 textmode))
124 (defun epg-context-set-include-certs (context include-certs)
125 "Set how many certificates should be included in an S/MIME signed message."
126 (aset context 3 include-certs))
128 (defun epg-context-set-passphrase-callback (context passphrase-callback
130 "Set the function used to query passphrase."
131 (aset context 4 (cons passphrase-callback handback)))
133 (defun epg-context-set-progress-callback (context progress-callback
135 "Set the function which handles progress update."
136 (aset context 5 (cons progress-callback handback)))
138 (defun epg-context-set-signers (context signers)
139 "Set the list of key-id for singning."
140 (aset context 6 signers))
142 (defun epg-context-set-process (context process)
143 "Set the process object of `epg-gpg-program'.
144 This function is for internal use only."
145 (aset context 7 process))
147 (defun epg-context-set-output-file (context output-file)
148 "Set the output file of `epg-gpg-program'.
149 This function is for internal use only."
150 (aset context 8 output-file))
152 (defun epg-context-set-result (context name result)
153 "Set the result of the previous cryptographic operation."
154 (let ((entry (assq name (aref context 9))))
156 (setcdr entry result)
157 (aset context 9 (cons (cons name result) (aref context 9))))))
159 (defun epg-make-signature (status key-id user-id)
160 "Return a signature object."
161 (vector status key-id user-id nil))
163 (defun epg-signature-status (signature)
164 "Return the status code of SIGNATURE."
167 (defun epg-signature-key-id (signature)
168 "Return the key-id of SIGNATURE."
171 (defun epg-signature-user-id (signature)
172 "Return the user-id of SIGNATURE."
175 (defun epg-signature-validity (signature)
176 "Return the validity of SIGNATURE."
179 (defun epg-signature-set-status (signature status)
180 "Set the status code of SIGNATURE."
181 (aset signature 0 status))
183 (defun epg-signature-set-key-id (signature key-id)
184 "Set the key-id of SIGNATURE."
185 (aset signature 1 key-id))
187 (defun epg-signature-set-user-id (signature user-id)
188 "Set the user-id of SIGNATURE."
189 (aset signature 2 user-id))
191 (defun epg-signature-set-validity (signature validity)
192 "Set the validity of SIGNATURE."
193 (aset signature 3 validity))
195 (defun epg-start (context args)
196 "Start `epg-gpg-program' in a subprocess with given ARGS."
197 (let* ((args (append (list "--no-tty"
201 (if (epg-context-armor context) '("--armor"))
202 (if (epg-context-textmode context) '("--textmode"))
203 (if (epg-context-output-file context)
204 (list "--output" (epg-context-output-file context)))
206 (coding-system-for-write 'binary)
207 process-connection-type
208 (orig-mode (default-file-modes))
209 (buffer (generate-new-buffer " *epg*"))
211 (with-current-buffer buffer
212 (make-local-variable 'epg-read-point)
213 (setq epg-read-point (point-min))
214 (make-local-variable 'epg-pending-status-list)
215 (setq epg-pending-status-list nil)
216 (make-local-variable 'epg-key-id)
217 (setq epg-key-id nil)
218 (make-local-variable 'epg-context)
219 (setq epg-context context))
222 (set-default-file-modes 448)
224 (apply #'start-process "epg" buffer epg-gpg-program args)))
225 (set-default-file-modes orig-mode))
226 (set-process-filter process #'epg-process-filter)
227 (epg-context-set-process context process)))
229 (defun epg-process-filter (process input)
232 (set-buffer (get-buffer-create " *epg-debug*"))
233 (goto-char (point-max))
235 (if (buffer-live-p (process-buffer process))
237 (set-buffer (process-buffer process))
238 (goto-char (point-max))
240 (goto-char epg-read-point)
242 (while (looking-at ".*\n") ;the input line is finished
244 (if (looking-at "\\[GNUPG:] \\([A-Z_]+\\) ?\\(.*\\)")
245 (let* ((status (match-string 1))
246 (string (match-string 2))
247 (symbol (intern-soft (concat "epg-status-" status))))
248 (if (member status epg-pending-status-list)
249 (setq epg-pending-status-list nil))
252 (funcall symbol process string)))))
254 (setq epg-read-point (point)))))
256 (defun epg-read-output (context)
258 (set-buffer-multibyte nil)
259 (if (file-exists-p (epg-context-output-file context))
260 (let ((coding-system-for-read (if (epg-context-output-file context)
263 (insert-file-contents (epg-context-output-file context))
266 (defun epg-wait-for-status (context status-list)
267 (with-current-buffer (process-buffer (epg-context-process context))
268 (setq epg-pending-status-list status-list)
269 (while (and (eq (process-status (epg-context-process context)) 'run)
270 epg-pending-status-list)
271 (accept-process-output (epg-context-process context) 1))))
273 (defun epg-wait-for-completion (context)
274 (process-send-eof (epg-context-process context))
275 (while (eq (process-status (epg-context-process context)) 'run)
276 ;; We can't use accept-process-output instead of sit-for here
277 ;; because it may cause an interrupt during the sentinel execution.
280 (defun epg-reset (context)
281 (if (and (epg-context-process context)
282 (buffer-live-p (process-buffer (epg-context-process context))))
283 (kill-buffer (process-buffer (epg-context-process context))))
284 (epg-context-set-process context nil)
285 (if (file-exists-p (epg-context-output-file context))
286 (delete-file (epg-context-output-file context)))
287 (aset context 9 nil))
289 (defun epg-status-USERID_HINT (process string)
290 (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
291 (let* ((key-id (match-string 1 string))
292 (user-id (match-string 2 string))
293 (entry (assoc key-id epg-user-id-alist)))
295 (setcdr entry user-id)
296 (setq epg-user-id-alist (cons (cons key-id user-id)
297 epg-user-id-alist))))))
299 (defun epg-status-NEED_PASSPHRASE (process string)
300 (if (string-match "\\`\\([^ ]+\\)" string)
301 (setq epg-key-id (match-string 1 string))))
303 (defun epg-status-NEED_PASSPHRASE_SYM (process string)
304 (setq epg-key-id 'SYM))
306 (defun epg-status-NEED_PASSPHRASE_PIN (process string)
307 (setq epg-key-id 'PIN))
309 (defun epg-status-GET_HIDDEN (process string)
310 (let ((passphrase (funcall
311 (car (epg-context-passphrase-callback epg-context))
313 (cdr (epg-context-passphrase-callback epg-context)))))
316 (process-send-string process (concat passphrase "\n")))
317 (fillarray passphrase 0))))
319 (defun epg-status-GOODSIG (process string)
320 (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
321 (epg-context-set-result
324 (cons (epg-make-signature 'good
325 (match-string 1 string)
326 (match-string 2 string))
327 (epg-context-result epg-context 'verify)))))
329 (defun epg-status-EXPSIG (process string)
330 (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
331 (epg-context-set-result
334 (cons (epg-make-signature 'expired
335 (match-string 1 string)
336 (match-string 2 string))
337 (epg-context-result epg-context 'verify)))))
339 (defun epg-status-EXPKEYSIG (process string)
340 (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
341 (epg-context-set-result
344 (cons (epg-make-signature 'expired-key
345 (match-string 1 string)
346 (match-string 2 string))
347 (epg-context-result epg-context 'verify)))))
349 (defun epg-status-REVKEYSIG (process string)
350 (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
351 (epg-context-set-result
354 (cons (epg-make-signature 'revoked-key
355 (match-string 1 string)
356 (match-string 2 string))
357 (epg-context-result epg-context 'verify)))))
359 (defun epg-status-BADSIG (process string)
360 (if (string-match "\\`\\([^ ]+\\) \\(.*\\)" string)
361 (epg-context-set-result
364 (cons (epg-make-signature 'bad
365 (match-string 1 string)
366 (match-string 2 string))
367 (epg-context-result epg-context 'verify)))))
369 (defun epg-status-TRUST_UNDEFINED (process string)
370 (let ((signature (car (epg-context-result epg-context 'verify))))
372 (eq (epg-signature-status signature) 'good))
373 (epg-signature-set-validity signature 'unknown))))
375 (defun epg-status-TRUST_NEVER (process string)
376 (let ((signature (car (epg-context-result epg-context 'verify))))
378 (eq (epg-signature-status signature) 'good))
379 (epg-signature-set-validity signature 'never))))
381 (defun epg-status-TRUST_MARGINAL (process string)
382 (let ((signature (car (epg-context-result epg-context 'verify))))
384 (eq (epg-signature-status signature) 'marginal))
385 (epg-signature-set-validity signature 'marginal))))
387 (defun epg-status-TRUST_FULLY (process string)
388 (let ((signature (car (epg-context-result epg-context 'verify))))
390 (eq (epg-signature-status signature) 'good))
391 (epg-signature-set-validity signature 'full))))
393 (defun epg-status-TRUST_ULTIMATE (process string)
394 (let ((signature (car (epg-context-result epg-context 'verify))))
396 (eq (epg-signature-status signature) 'good))
397 (epg-signature-set-validity signature 'full))))
399 (defun epg-status-DECRYPTION_FAILED (process string)
400 (epg-context-set-result epg-context 'decrypt 'failed))
402 (defun epg-status-PROGRESS (process string)
403 (if (string-match "\\`\\([^ ]+\\) \\([^ ]\\) \\([0-9]+\\) \\([0-9]+\\)"
405 (funcall (car (epg-context-progress-callback epg-context))
406 (match-string 1 string)
407 (match-string 2 string)
408 (string-to-number (match-string 3 string))
409 (string-to-number (match-string 4 string)))))
411 (defun epg-passphrase-callback (key-id handback)
414 "GnuPG passphrase for symmetric encryption: "
416 "GnuPG passphrase for PIN: "
417 (format "GnuPG passphrase for %s: "
418 (let ((entry (assoc key-id epg-user-id-alist)))
423 (defun epg-progress-callback (what char current total handback)
424 (message "%s: %d%%/%d%%" what current total))
426 (defun epg-list-keys (name &optional secret)
427 "List keys associated with STRING."
428 (let ((args (list "--with-colons" "--no-greeting" "--batch"
430 (if secret "--list-secret-keys" "--list-keys")
432 keys type symbol pointer)
434 (apply #'call-process epg-gpg-program nil (list t nil) nil args)
435 (goto-char (point-min))
436 (while (looking-at "\\([a-z][a-z][a-z]\\):\\(.*\\)")
437 (setq type (match-string 1)
438 symbol (intern-soft (format "epg-colons-%s-spec" type)))
439 (if (member type '("pub" "sec"))
440 (setq keys (cons nil keys)))
443 (setcar keys (cons (cons (intern type)
445 (symbol-value symbol)
451 (setcar pointer (nreverse (car pointer)))
452 (setq pointer (cdr pointer)))
455 (defun epg-parse-colons (alist string)
459 (or (null (car alist))
462 (concat "\\(" (nth 1 (car alist)) "\\)?:")
466 (setq index (match-end 0))
467 (if (match-beginning 1)
469 (cons (cons (car (car alist))
470 (funcall (or (nth 3 (car alist)) #'identity)
472 (1+ (or (nth 2 (car alist)) 0))
475 (setq index (1+ index)))
476 (setq alist (cdr alist)))
480 (defun epg-decrypt-start (context input-file)
481 "Initiate a decrypt operation."
482 (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
484 (list "--decrypt" input-file))
485 (epg-wait-for-status context '("BEGIN_DECRYPTION")))
488 (defun epg-decrypt-string (context string)
490 (let ((input-file (epg-make-temp-file "epg-input"))
491 (coding-system-for-write 'binary))
494 (write-region string nil input-file)
495 (epg-decrypt-start context input-file)
496 (epg-wait-for-completion context)
497 (unless (epg-context-result context 'decrypt)
498 (epg-read-output context)))
500 (if (file-exists-p input-file)
501 (delete-file input-file)))))
504 (defun epg-verify-start (context signature &optional string)
505 "Initiate a verify operation."
506 (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
508 ;; Detached signature.
511 (append (list "--verify")
512 (list signature "-")))
513 (if (eq (process-status (epg-context-process context)) 'run)
514 (process-send-string (epg-context-process context) string)))
515 ;; Normal (or cleartext) signature.
518 (if (eq (process-status (epg-context-process context)) 'run)
519 (process-send-string (epg-context-process context) signature))))
522 (defun epg-verify-string (context signature &optional string)
524 (let ((input-file (epg-make-temp-file "epg-input"))
525 (coding-system-for-write 'binary))
529 (write-region signature nil input-file))
530 (epg-verify-start context input-file string)
531 (epg-wait-for-completion context)
532 (epg-context-result context 'verify))
534 (if (file-exists-p input-file)
535 (delete-file input-file)))))
538 (defun epg-sign-start (context string &optional mode)
539 "Initiate a sign operation."
540 (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
542 (append (list (if (null mode)
544 (if (or (eq mode t) (eq mode 'detached))
548 (mapcar (lambda (signer)
550 (epg-context-signers context)))))
551 (epg-wait-for-status context '("BEGIN_SIGNING"))
552 (if (eq (process-status (epg-context-process context)) 'run)
553 (process-send-string (epg-context-process context) string)))
556 (defun epg-sign-string (context string &optional mode)
560 (epg-sign-start context string mode)
561 (epg-wait-for-completion context)
562 (epg-read-output context))
563 (epg-reset context)))
566 (defun epg-encrypt-start (context string recipients
567 &optional always-trust sign)
568 "Initiate a encrypt operation."
569 (epg-context-set-output-file context (epg-make-temp-file "epg-output"))
571 (append (if always-trust '("--always-trust"))
572 (if recipients '("--encrypt") '("--symmetric"))
576 (mapcar (lambda (signer)
578 (epg-context-signers context)))))
580 (mapcar (lambda (recipient)
581 (list "-r" recipient))
584 (epg-wait-for-status context '("BEGIN_SIGNING")))
585 (if (eq (process-status (epg-context-process context)) 'run)
586 (process-send-string (epg-context-process context) string)))
589 (defun epg-encrypt-string (context string recipients
590 &optional always-trust sign)
594 (epg-encrypt-start context string recipients always-trust sign)
595 (epg-wait-for-completion context)
596 (epg-read-output context))
597 (epg-reset context)))