Add 2012 to FSF copyright years for Emacs files
[bpt/emacs.git] / lisp / org / org-crypt.el
CommitLineData
8d642074
CD
1;;; org-crypt.el --- Public key encryption for org-mode entries
2
b9db31c7 3;; Copyright (C) 2007, 2009-2012 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))
76
77(defgroup org-crypt nil
78 "Org Crypt"
14e1337f 79 :tag "Org Crypt"
3ab2c837 80 :group 'org)
8d642074
CD
81
82(defcustom org-crypt-tag-matcher "crypt"
86fbb8ca
CD
83 "The tag matcher used to find headings whose contents should be encrypted.
84
85See the \"Match syntax\" section of the org manual for more details."
14e1337f 86 :type 'string
3ab2c837 87 :group 'org-crypt)
8d642074 88
3ab2c837 89(defcustom org-crypt-key ""
86fbb8ca
CD
90 "The default key to use when encrypting the contents of a heading.
91
92This setting can also be overridden in the CRYPTKEY property."
14e1337f 93 :type 'string
3ab2c837
BG
94 :group 'org-crypt)
95
96(defcustom org-crypt-disable-auto-save 'ask
97 "What org-decrypt should do if `auto-save-mode' is enabled.
98
99t : Disable auto-save-mode for the current buffer
100 prior to decrypting an entry.
101
102nil : Leave auto-save-mode enabled.
103 This may cause data to be written to disk unencrypted!
104
105'ask : Ask user whether or not to disable auto-save-mode
106 for the current buffer.
107
108'encrypt : Leave auto-save-mode enabled for the current buffer,
109 but automatically re-encrypt all decrypted entries
110 *before* auto-saving.
111 NOTE: This only works for entries which have a tag
112 that matches `org-crypt-tag-matcher'."
113 :group 'org-crypt
114 :type '(choice (const :tag "Always" t)
115 (const :tag "Never" nil)
116 (const :tag "Ask" ask)
117 (const :tag "Encrypt" encrypt)))
8d642074 118
e66ba1df
BG
119(defun org-crypt-check-auto-save ()
120 "Check whether auto-save-mode is enabled for the current buffer.
121
122`auto-save-mode' may cause leakage when decrypting entries, so
123check whether it's enabled, and decide what to do about it.
124
125See `org-crypt-disable-auto-save'."
126 (when buffer-auto-save-file-name
127 (cond
128 ((or
129 (eq org-crypt-disable-auto-save t)
130 (and
131 (eq org-crypt-disable-auto-save 'ask)
132 (y-or-n-p "org-decrypt: auto-save-mode may cause leakage. Disable it for current buffer? ")))
133 (message (concat "org-decrypt: Disabling auto-save-mode for " (or (buffer-file-name) (current-buffer))))
134 ; The argument to auto-save-mode has to be "-1", since
135 ; giving a "nil" argument toggles instead of disabling.
136 (auto-save-mode -1))
137 ((eq org-crypt-disable-auto-save nil)
138 (message "org-decrypt: Decrypting entry with auto-save-mode enabled. This may cause leakage."))
139 ((eq org-crypt-disable-auto-save 'encrypt)
140 (message "org-decrypt: Enabling re-encryption on auto-save.")
141 (add-hook 'auto-save-hook
142 (lambda ()
143 (message "org-crypt: Re-encrypting all decrypted entries due to auto-save.")
144 (org-encrypt-entries))
145 nil t))
146 (t nil))))
147
8d642074 148(defun org-crypt-key-for-heading ()
86fbb8ca 149 "Return the encryption key for the current heading."
8d642074
CD
150 (save-excursion
151 (org-back-to-heading t)
ed21c5c8 152 (or (org-entry-get nil "CRYPTKEY" 'selective)
8d642074
CD
153 org-crypt-key
154 (and (boundp 'epa-file-encrypt-to) epa-file-encrypt-to)
86fbb8ca 155 (message "No crypt key set, using symmetric encryption."))))
8d642074 156
3ab2c837
BG
157(defun org-encrypt-string (str crypt-key)
158 "Return STR encrypted with CRYPT-KEY."
159 ;; Text and key have to be identical, otherwise we re-crypt.
160 (if (and (string= crypt-key (get-text-property 0 'org-crypt-key str))
161 (string= (sha1 str) (get-text-property 0 'org-crypt-checksum str)))
162 (get-text-property 0 'org-crypt-text str)
163 (let ((epg-context (epg-make-context nil t t)))
164 (epg-encrypt-string epg-context str (epg-list-keys epg-context crypt-key)))))
165
8d642074
CD
166(defun org-encrypt-entry ()
167 "Encrypt the content of the current headline."
168 (interactive)
169 (require 'epg)
170 (save-excursion
171 (org-back-to-heading t)
86fbb8ca
CD
172 (let ((start-heading (point)))
173 (forward-line)
174 (when (not (looking-at "-----BEGIN PGP MESSAGE-----"))
3ab2c837 175 (let ((folded (outline-invisible-p))
86fbb8ca
CD
176 (epg-context (epg-make-context nil t t))
177 (crypt-key (org-crypt-key-for-heading))
178 (beg (point))
179 end encrypted-text)
180 (goto-char start-heading)
181 (org-end-of-subtree t t)
182 (org-back-over-empty-lines)
183 (setq end (point)
184 encrypted-text
3ab2c837 185 (org-encrypt-string (buffer-substring beg end) crypt-key))
86fbb8ca
CD
186 (delete-region beg end)
187 (insert encrypted-text)
188 (when folded
189 (goto-char start-heading)
190 (hide-subtree))
191 nil)))))
8d642074
CD
192
193(defun org-decrypt-entry ()
ed21c5c8 194 "Decrypt the content of the current headline."
8d642074
CD
195 (interactive)
196 (require 'epg)
86fbb8ca
CD
197 (unless (org-before-first-heading-p)
198 (save-excursion
199 (org-back-to-heading t)
3ab2c837
BG
200 (let ((heading-point (point))
201 (heading-was-invisible-p
202 (save-excursion
203 (outline-end-of-heading)
204 (outline-invisible-p))))
205 (forward-line)
206 (when (looking-at "-----BEGIN PGP MESSAGE-----")
e66ba1df 207 (org-crypt-check-auto-save)
3ab2c837
BG
208 (let* ((end (save-excursion
209 (search-forward "-----END PGP MESSAGE-----")
210 (forward-line)
211 (point)))
212 (epg-context (epg-make-context nil t t))
213 (encrypted-text (buffer-substring-no-properties (point) end))
214 (decrypted-text
215 (decode-coding-string
216 (epg-decrypt-string
217 epg-context
218 encrypted-text)
219 'utf-8)))
220 ;; Delete region starting just before point, because the
221 ;; outline property starts at the \n of the heading.
222 (delete-region (1- (point)) end)
223 ;; Store a checksum of the decrypted and the encrypted
224 ;; text value. This allow to reuse the same encrypted text
225 ;; if the text does not change, and therefore avoid a
226 ;; re-encryption process.
227 (insert "\n" (propertize decrypted-text
228 'org-crypt-checksum (sha1 decrypted-text)
229 'org-crypt-key (org-crypt-key-for-heading)
230 'org-crypt-text encrypted-text))
231 (when heading-was-invisible-p
232 (goto-char heading-point)
233 (org-flag-subtree t))
234 nil))))))
8d642074
CD
235
236(defun org-encrypt-entries ()
ed21c5c8 237 "Encrypt all top-level entries in the current buffer."
8d642074
CD
238 (interactive)
239 (org-scan-tags
240 'org-encrypt-entry
241 (cdr (org-make-tags-matcher org-crypt-tag-matcher))))
242
243(defun org-decrypt-entries ()
ed21c5c8 244 "Decrypt all entries in the current buffer."
8d642074 245 (interactive)
ed21c5c8 246 (org-scan-tags
8d642074
CD
247 'org-decrypt-entry
248 (cdr (org-make-tags-matcher org-crypt-tag-matcher))))
249
250(defun org-crypt-use-before-save-magic ()
86fbb8ca 251 "Add a hook to automatically encrypt entries before a file is saved to disk."
ed21c5c8
CD
252 (add-hook
253 'org-mode-hook
8d642074 254 (lambda () (add-hook 'before-save-hook 'org-encrypt-entries nil t))))
ed21c5c8
CD
255
256(add-hook 'org-reveal-start-hook 'org-decrypt-entry)
257
8d642074
CD
258(provide 'org-crypt)
259
8d642074 260;;; org-crypt.el ends here