mirror of https://github.com/Chizi123/.emacs.d.git

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