* etc/publicsuffix.txt: Update from source.
[bpt/emacs.git] / lisp / org / org-crypt.el
CommitLineData
8d642074
CD
1;;; org-crypt.el --- Public key encryption for org-mode entries
2
ba318903 3;; Copyright (C) 2007-2014 Free Software Foundation, Inc.
8d642074
CD
4
5;; Emacs Lisp Archive Entry
6;; Filename: org-crypt.el
8d642074
CD
7;; Keywords: org-mode
8;; Author: John Wiegley <johnw@gnu.org>
9;; Maintainer: Peter Jones <pjones@pmade.com>
10;; Description: Adds public key encryption to org-mode buffers
11;; URL: http://www.newartisans.com/software/emacs.html
12;; Compatibility: Emacs22
13
14;; This file is part of GNU Emacs.
15;;
16;; GNU Emacs is free software: you can redistribute it and/or modify
17;; it under the terms of the GNU General Public License as published by
18;; the Free Software Foundation, either version 3 of the License, or
19;; (at your option) any later version.
20
21;; GNU Emacs is distributed in the hope that it will be useful,
22;; but WITHOUT ANY WARRANTY; without even the implied warranty of
23;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24;; GNU General Public License for more details.
25
26;; You should have received a copy of the GNU General Public License
27;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
28
29;;; Commentary:
30
31;; Right now this is just a set of functions to play with. It depends
32;; on the epg library. Here's how you would use it:
33;;
34;; 1. To mark an entry for encryption, tag the heading with "crypt".
35;; You can change the tag to any complex tag matching string by
36;; setting the `org-crypt-tag-matcher' variable.
37;;
38;; 2. Set the encryption key to use in the `org-crypt-key' variable,
39;; or use `M-x org-set-property' to set the property CRYPTKEY to
40;; any address in your public keyring. The text of the entry (but
41;; not its properties or headline) will be encrypted for this user.
42;; For them to read it, the corresponding secret key must be
43;; located in the secret key ring of the account where you try to
44;; decrypt it. This makes it possible to leave secure notes that
45;; only the intended recipient can read in a shared-org-mode-files
46;; scenario.
86fbb8ca 47;; If the key is not set, org-crypt will default to symmetric encryption.
8d642074
CD
48;;
49;; 3. To later decrypt an entry, use `org-decrypt-entries' or
50;; `org-decrypt-entry'. It might be useful to bind this to a key,
51;; like C-c C-/. I hope that in the future, C-c C-r can be might
52;; overloaded to also decrypt an entry if it's encrypted, since
53;; that fits nicely with the meaning of "reveal".
54;;
55;; 4. To automatically encrypt all necessary entries when saving a
56;; file, call `org-crypt-use-before-save-magic' after loading
57;; org-crypt.el.
8d642074
CD
58
59;;; Thanks:
60
61;; - Carsten Dominik
62;; - Vitaly Ostanin
63
64(require 'org)
65
86fbb8ca
CD
66;;; Code:
67
8d642074
CD
68(declare-function epg-decrypt-string "epg" (context cipher))
69(declare-function epg-list-keys "epg" (context &optional name mode))
70(declare-function epg-make-context "epg"
71 (&optional protocol armor textmode include-certs
72 cipher-algorithm digest-algorithm
73 compress-algorithm))
74(declare-function epg-encrypt-string "epg"
75 (context plain recipients &optional sign always-trust))
73d3db82
BG
76(defvar epg-context)
77
8d642074
CD
78
79(defgroup org-crypt nil
8223b1d2 80 "Org Crypt."
14e1337f 81 :tag "Org Crypt"
3ab2c837 82 :group 'org)
8d642074
CD
83
84(defcustom org-crypt-tag-matcher "crypt"
86fbb8ca
CD
85 "The tag matcher used to find headings whose contents should be encrypted.
86
87See the \"Match syntax\" section of the org manual for more details."
14e1337f 88 :type 'string
3ab2c837 89 :group 'org-crypt)
8d642074 90
3ab2c837 91(defcustom org-crypt-key ""
86fbb8ca
CD
92 "The default key to use when encrypting the contents of a heading.
93
94This setting can also be overridden in the CRYPTKEY property."
14e1337f 95 :type 'string
3ab2c837
BG
96 :group 'org-crypt)
97
98(defcustom org-crypt-disable-auto-save 'ask
99 "What org-decrypt should do if `auto-save-mode' is enabled.
100
101t : Disable auto-save-mode for the current buffer
102 prior to decrypting an entry.
103
104nil : Leave auto-save-mode enabled.
105 This may cause data to be written to disk unencrypted!
106
107'ask : Ask user whether or not to disable auto-save-mode
108 for the current buffer.
109
110'encrypt : Leave auto-save-mode enabled for the current buffer,
111 but automatically re-encrypt all decrypted entries
112 *before* auto-saving.
113 NOTE: This only works for entries which have a tag
114 that matches `org-crypt-tag-matcher'."
115 :group 'org-crypt
8223b1d2 116 :version "24.1"
3ab2c837
BG
117 :type '(choice (const :tag "Always" t)
118 (const :tag "Never" nil)
119 (const :tag "Ask" ask)
120 (const :tag "Encrypt" encrypt)))
8d642074 121
e66ba1df
BG
122(defun org-crypt-check-auto-save ()
123 "Check whether auto-save-mode is enabled for the current buffer.
124
125`auto-save-mode' may cause leakage when decrypting entries, so
126check whether it's enabled, and decide what to do about it.
127
128See `org-crypt-disable-auto-save'."
129 (when buffer-auto-save-file-name
130 (cond
131 ((or
132 (eq org-crypt-disable-auto-save t)
133 (and
134 (eq org-crypt-disable-auto-save 'ask)
8223b1d2 135 (y-or-n-p "org-decrypt: auto-save-mode may cause leakage. Disable it for current buffer? ")))
e66ba1df 136 (message (concat "org-decrypt: Disabling auto-save-mode for " (or (buffer-file-name) (current-buffer))))
8223b1d2
BG
137 ; The argument to auto-save-mode has to be "-1", since
138 ; giving a "nil" argument toggles instead of disabling.
e66ba1df
BG
139 (auto-save-mode -1))
140 ((eq org-crypt-disable-auto-save nil)
8223b1d2 141 (message "org-decrypt: Decrypting entry with auto-save-mode enabled. This may cause leakage."))
e66ba1df
BG
142 ((eq org-crypt-disable-auto-save 'encrypt)
143 (message "org-decrypt: Enabling re-encryption on auto-save.")
271672fa
BG
144 (org-add-hook 'auto-save-hook
145 (lambda ()
146 (message "org-crypt: Re-encrypting all decrypted entries due to auto-save.")
147 (org-encrypt-entries))
148 nil t))
e66ba1df
BG
149 (t nil))))
150
8d642074 151(defun org-crypt-key-for-heading ()
86fbb8ca 152 "Return the encryption key for the current heading."
8d642074
CD
153 (save-excursion
154 (org-back-to-heading t)
ed21c5c8 155 (or (org-entry-get nil "CRYPTKEY" 'selective)
8d642074
CD
156 org-crypt-key
157 (and (boundp 'epa-file-encrypt-to) epa-file-encrypt-to)
86fbb8ca 158 (message "No crypt key set, using symmetric encryption."))))
8d642074 159
3ab2c837
BG
160(defun org-encrypt-string (str crypt-key)
161 "Return STR encrypted with CRYPT-KEY."
162 ;; Text and key have to be identical, otherwise we re-crypt.
163 (if (and (string= crypt-key (get-text-property 0 'org-crypt-key str))
164 (string= (sha1 str) (get-text-property 0 'org-crypt-checksum str)))
165 (get-text-property 0 'org-crypt-text str)
73d3db82
BG
166 (set (make-local-variable 'epg-context) (epg-make-context nil t t))
167 (epg-encrypt-string epg-context str (epg-list-keys epg-context crypt-key))))
3ab2c837 168
8d642074
CD
169(defun org-encrypt-entry ()
170 "Encrypt the content of the current headline."
171 (interactive)
172 (require 'epg)
173 (save-excursion
174 (org-back-to-heading t)
73d3db82 175 (set (make-local-variable 'epg-context) (epg-make-context nil t t))
86fbb8ca
CD
176 (let ((start-heading (point)))
177 (forward-line)
178 (when (not (looking-at "-----BEGIN PGP MESSAGE-----"))
3ab2c837 179 (let ((folded (outline-invisible-p))
86fbb8ca
CD
180 (crypt-key (org-crypt-key-for-heading))
181 (beg (point))
182 end encrypted-text)
183 (goto-char start-heading)
184 (org-end-of-subtree t t)
185 (org-back-over-empty-lines)
186 (setq end (point)
187 encrypted-text
3ab2c837 188 (org-encrypt-string (buffer-substring beg end) crypt-key))
86fbb8ca
CD
189 (delete-region beg end)
190 (insert encrypted-text)
191 (when folded
192 (goto-char start-heading)
193 (hide-subtree))
194 nil)))))
8d642074
CD
195
196(defun org-decrypt-entry ()
ed21c5c8 197 "Decrypt the content of the current headline."
8d642074
CD
198 (interactive)
199 (require 'epg)
86fbb8ca
CD
200 (unless (org-before-first-heading-p)
201 (save-excursion
202 (org-back-to-heading t)
3ab2c837
BG
203 (let ((heading-point (point))
204 (heading-was-invisible-p
205 (save-excursion
206 (outline-end-of-heading)
207 (outline-invisible-p))))
208 (forward-line)
209 (when (looking-at "-----BEGIN PGP MESSAGE-----")
e66ba1df 210 (org-crypt-check-auto-save)
73d3db82 211 (set (make-local-variable 'epg-context) (epg-make-context nil t t))
3ab2c837
BG
212 (let* ((end (save-excursion
213 (search-forward "-----END PGP MESSAGE-----")
214 (forward-line)
215 (point)))
3ab2c837
BG
216 (encrypted-text (buffer-substring-no-properties (point) end))
217 (decrypted-text
218 (decode-coding-string
219 (epg-decrypt-string
220 epg-context
221 encrypted-text)
222 'utf-8)))
223 ;; Delete region starting just before point, because the
224 ;; outline property starts at the \n of the heading.
225 (delete-region (1- (point)) end)
226 ;; Store a checksum of the decrypted and the encrypted
8223b1d2 227 ;; text value. This allow to reuse the same encrypted text
3ab2c837
BG
228 ;; if the text does not change, and therefore avoid a
229 ;; re-encryption process.
230 (insert "\n" (propertize decrypted-text
231 'org-crypt-checksum (sha1 decrypted-text)
232 'org-crypt-key (org-crypt-key-for-heading)
233 'org-crypt-text encrypted-text))
234 (when heading-was-invisible-p
235 (goto-char heading-point)
236 (org-flag-subtree t))
237 nil))))))
8d642074
CD
238
239(defun org-encrypt-entries ()
ed21c5c8 240 "Encrypt all top-level entries in the current buffer."
8d642074 241 (interactive)
153ae947
BG
242 (let (todo-only)
243 (org-scan-tags
244 'org-encrypt-entry
245 (cdr (org-make-tags-matcher org-crypt-tag-matcher))
246 todo-only)))
8d642074
CD
247
248(defun org-decrypt-entries ()
ed21c5c8 249 "Decrypt all entries in the current buffer."
8d642074 250 (interactive)
153ae947
BG
251 (let (todo-only)
252 (org-scan-tags
253 'org-decrypt-entry
254 (cdr (org-make-tags-matcher org-crypt-tag-matcher))
255 todo-only)))
8d642074 256
8223b1d2
BG
257(defun org-at-encrypted-entry-p ()
258 "Is the current entry encrypted?"
259 (unless (org-before-first-heading-p)
260 (save-excursion
261 (org-back-to-heading t)
262 (search-forward "-----BEGIN PGP MESSAGE-----"
c7cf0ebc 263 (save-excursion (outline-next-heading)) t))))
8223b1d2 264
8d642074 265(defun org-crypt-use-before-save-magic ()
86fbb8ca 266 "Add a hook to automatically encrypt entries before a file is saved to disk."
ed21c5c8
CD
267 (add-hook
268 'org-mode-hook
271672fa 269 (lambda () (org-add-hook 'before-save-hook 'org-encrypt-entries nil t))))
ed21c5c8
CD
270
271(add-hook 'org-reveal-start-hook 'org-decrypt-entry)
272
8d642074
CD
273(provide 'org-crypt)
274
8d642074 275;;; org-crypt.el ends here