diff options
author | Bastien Guerry <bzg@gnu.org> | 2020-12-13 13:44:15 +0100 |
---|---|---|
committer | Bastien Guerry <bzg@gnu.org> | 2020-12-13 13:44:15 +0100 |
commit | f22856a5c54d99867cd24c08a14bbda23d5c6229 (patch) | |
tree | b6bd688963531eccb8b9d18195df0edfc34ba59d /lisp/org/org-crypt.el | |
parent | 6aa9fe3e1b4052b2acde86404a90e35893ebfa00 (diff) | |
download | emacs-f22856a5c54d99867cd24c08a14bbda23d5c6229.tar.gz emacs-f22856a5c54d99867cd24c08a14bbda23d5c6229.tar.bz2 emacs-f22856a5c54d99867cd24c08a14bbda23d5c6229.zip |
Update to Org 9.4.1
Diffstat (limited to 'lisp/org/org-crypt.el')
-rw-r--r-- | lisp/org/org-crypt.el | 231 |
1 files changed, 137 insertions, 94 deletions
diff --git a/lisp/org/org-crypt.el b/lisp/org/org-crypt.el index 4b46a3145f4..187560c5538 100644 --- a/lisp/org/org-crypt.el +++ b/lisp/org/org-crypt.el @@ -1,14 +1,8 @@ ;;; org-crypt.el --- Public Key Encryption for Org Entries -*- lexical-binding: t; -*- +;; ;; Copyright (C) 2007-2020 Free Software Foundation, Inc. -;; Emacs Lisp Archive Entry -;; Filename: org-crypt.el -;; Keywords: org-mode ;; Author: John Wiegley <johnw@gnu.org> -;; Maintainer: Peter Jones <pjones@pmade.com> -;; Description: Adds public key encryption to Org buffers -;; URL: http://www.newartisans.com/software/emacs.html -;; Compatibility: Emacs22 ;; This file is part of GNU Emacs. ;; @@ -47,9 +41,7 @@ ;; ;; 3. To later decrypt an entry, use `org-decrypt-entries' or ;; `org-decrypt-entry'. It might be useful to bind this to a key, -;; like C-c C-/. I hope that in the future, C-c C-r can be might -;; overloaded to also decrypt an entry if it's encrypted, since -;; that fits nicely with the meaning of "reveal". +;; like C-c C-/. ;; ;; 4. To automatically encrypt all necessary entries when saving a ;; file, call `org-crypt-use-before-save-magic' after loading @@ -60,10 +52,11 @@ ;; - Carsten Dominik ;; - Vitaly Ostanin -(require 'org) - ;;; Code: +(require 'org-macs) +(require 'org-compat) + (declare-function epg-decrypt-string "epg" (context cipher)) (declare-function epg-list-keys "epg" (context &optional name mode)) (declare-function epg-make-context "epg" @@ -74,6 +67,17 @@ (context plain recipients &optional sign always-trust)) (defvar epg-context) +(declare-function org-back-over-empty-lines "org" ()) +(declare-function org-back-to-heading "org" (&optional invisible-ok)) +(declare-function org-before-first-heading-p "org" ()) +(declare-function org-end-of-meta-data "org" (&optional full)) +(declare-function org-end-of-subtree "org" (&optional invisible-ok to-heading)) +(declare-function org-entry-get "org" (pom property &optional inherit literal-nil)) +(declare-function org-flag-subtree "org" (flag)) +(declare-function org-make-tags-matcher "org" (match)) +(declare-function org-previous-visible-heading "org" (arg)) +(declare-function org-scan-tags "org" (action matcher todo-only &optional start-level)) +(declare-function org-set-property "org" (property value)) (defgroup org-crypt nil "Org Crypt." @@ -90,9 +94,18 @@ See the \"Match syntax\" section of the org manual for more details." (defcustom org-crypt-key "" "The default key to use when encrypting the contents of a heading. -This setting can also be overridden in the CRYPTKEY property." - :type 'string - :group 'org-crypt) +If this variable is nil, always use symmetric encryption, unconditionally. + +Otherwise, The string is matched against all keys in the key ring. +In particular, the empty string matches no key. If no key is found, +look for the `epa-file-encrypt-to' local variable. Ultimately fall back +to symmetric encryption. + +This setting can be overridden in the CRYPTKEY property." + :group 'org-crypt + :type '(choice + (string :tag "Public key(s) matching") + (const :tag "Symmetric encryption" nil))) (defcustom org-crypt-disable-auto-save 'ask "What org-decrypt should do if `auto-save-mode' is enabled. @@ -118,6 +131,36 @@ nil : Leave auto-save-mode enabled. (const :tag "Ask" ask) (const :tag "Encrypt" encrypt))) +(defun org-crypt--encrypted-text (beg end) + "Return encrypted text in between BEG and END." + ;; Ignore indentation. + (replace-regexp-in-string + "^[ \t]*" "" + (buffer-substring-no-properties beg end))) + +(defun org-at-encrypted-entry-p () + "Is the current entry encrypted? +When the entry is encrypted, return a pair (BEG . END) where BEG +and END are buffer positions delimiting the encrypted area." + (org-with-wide-buffer + (unless (org-before-first-heading-p) + (org-back-to-heading t) + (org-end-of-meta-data 'standard) + (let ((case-fold-search nil) + (banner-start (rx (seq bol + (zero-or-more (any "\t ")) + "-----BEGIN PGP MESSAGE-----" + eol)))) + (when (looking-at banner-start) + (let ((start (point)) + (banner-end (rx (seq bol + (or (group (zero-or-more (any "\t ")) + "-----END PGP MESSAGE-----" + eol) + (seq (one-or-more "*") " ")))))) + (when (and (re-search-forward banner-end nil t) (match-string 1)) + (cons start (line-beginning-position 2))))))))) + (defun org-crypt-check-auto-save () "Check whether auto-save-mode is enabled for the current buffer. @@ -149,93 +192,99 @@ See `org-crypt-disable-auto-save'." (t nil)))) (defun org-crypt-key-for-heading () - "Return the encryption key for the current heading." - (save-excursion - (org-back-to-heading t) - (or (org-entry-get nil "CRYPTKEY" 'selective) - org-crypt-key - (and (boundp 'epa-file-encrypt-to) epa-file-encrypt-to) - (message "No crypt key set, using symmetric encryption.")))) - -(defun org-encrypt-string (str crypt-key) - "Return STR encrypted with CRYPT-KEY." - ;; Text and key have to be identical, otherwise we re-crypt. - (if (and (string= crypt-key (get-text-property 0 'org-crypt-key str)) - (string= (sha1 str) (get-text-property 0 'org-crypt-checksum str))) - (get-text-property 0 'org-crypt-text str) - (setq-local epg-context (epg-make-context nil t t)) - (epg-encrypt-string epg-context str (epg-list-keys epg-context crypt-key)))) - + "Return the encryption key(s) for the current heading. +Assume `epg-context' is set." + (and org-crypt-key + (or (epg-list-keys epg-context + (or (org-entry-get nil "CRYPTKEY" 'selective) + org-crypt-key)) + (bound-and-true-p epa-file-encrypt-to) + (progn + (message "No crypt key set, using symmetric encryption.") + nil)))) + +;;;###autoload (defun org-encrypt-entry () "Encrypt the content of the current headline." (interactive) - (require 'epg) - (org-with-wide-buffer - (org-back-to-heading t) - (setq-local epg-context (epg-make-context nil t t)) - (let ((start-heading (point))) - (org-end-of-meta-data) - (unless (looking-at-p "-----BEGIN PGP MESSAGE-----") - (let ((folded (org-invisible-p)) - (crypt-key (org-crypt-key-for-heading)) - (beg (point))) + (unless (org-at-encrypted-entry-p) + (require 'epg) + (setq-local epg-context (epg-make-context nil t t)) + (org-with-wide-buffer + (org-back-to-heading t) + (let ((start-heading (point)) + (crypt-key (org-crypt-key-for-heading)) + (folded? (org-invisible-p (line-beginning-position)))) + (org-end-of-meta-data 'standard) + (let ((beg (point)) + (folded-heading + (and folded? + (save-excursion + (org-previous-visible-heading 1) + (point))))) (goto-char start-heading) (org-end-of-subtree t t) (org-back-over-empty-lines) - (let ((contents (delete-and-extract-region beg (point)))) + (let* ((contents (delete-and-extract-region beg (point))) + (key (get-text-property 0 'org-crypt-key contents)) + (checksum (get-text-property 0 'org-crypt-checksum contents))) (condition-case err - (insert (org-encrypt-string contents crypt-key)) + (insert + ;; Text and key have to be identical, otherwise we + ;; re-crypt. + (if (and (equal crypt-key key) + (string= checksum (sha1 contents))) + (get-text-property 0 'org-crypt-text contents) + (epg-encrypt-string epg-context contents crypt-key))) ;; If encryption failed, make sure to insert back entry ;; contents in the buffer. - (error (insert contents) (error (nth 1 err))))) - (when folded - (goto-char start-heading) + (error + (insert contents) + (error (error-message-string err))))) + (when folded-heading + (goto-char folded-heading) (org-flag-subtree t)) nil))))) +;;;###autoload (defun org-decrypt-entry () "Decrypt the content of the current headline." (interactive) - (require 'epg) - (unless (org-before-first-heading-p) - (org-with-wide-buffer - (org-back-to-heading t) - (let ((heading-point (point)) - (heading-was-invisible-p - (save-excursion - (outline-end-of-heading) - (org-invisible-p)))) - (org-end-of-meta-data) - (when (looking-at "-----BEGIN PGP MESSAGE-----") - (org-crypt-check-auto-save) - (setq-local epg-context (epg-make-context nil t t)) - (let* ((end (save-excursion - (search-forward "-----END PGP MESSAGE-----") - (forward-line) - (point))) - (encrypted-text (buffer-substring-no-properties (point) end)) - (decrypted-text - (decode-coding-string - (epg-decrypt-string - epg-context - encrypted-text) - 'utf-8))) - ;; Delete region starting just before point, because the - ;; outline property starts at the \n of the heading. - (delete-region (1- (point)) end) - ;; Store a checksum of the decrypted and the encrypted - ;; text value. This allows reusing the same encrypted text - ;; if the text does not change, and therefore avoid a - ;; re-encryption process. - (insert "\n" (propertize decrypted-text - 'org-crypt-checksum (sha1 decrypted-text) - 'org-crypt-key (org-crypt-key-for-heading) - 'org-crypt-text encrypted-text)) - (when heading-was-invisible-p - (goto-char heading-point) - (org-flag-subtree t)) - nil)))))) + (pcase (org-at-encrypted-entry-p) + (`(,beg . ,end) + (require 'epg) + (setq-local epg-context (epg-make-context nil t t)) + (org-with-point-at beg + (org-crypt-check-auto-save) + (let* ((folded-heading + (and (org-invisible-p) + (save-excursion + (org-previous-visible-heading 1) + (point)))) + (encrypted-text (org-crypt--encrypted-text beg end)) + (decrypted-text + (decode-coding-string + (epg-decrypt-string epg-context encrypted-text) + 'utf-8))) + ;; Delete region starting just before point, because the + ;; outline property starts at the \n of the heading. + (delete-region (1- (point)) end) + ;; Store a checksum of the decrypted and the encrypted text + ;; value. This allows reusing the same encrypted text if the + ;; text does not change, and therefore avoid a re-encryption + ;; process. + (insert "\n" + (propertize decrypted-text + 'org-crypt-checksum (sha1 decrypted-text) + 'org-crypt-key (org-crypt-key-for-heading) + 'org-crypt-text encrypted-text)) + (when folded-heading + (goto-char folded-heading) + (org-flag-subtree t)) + nil))) + (_ nil))) +;;;###autoload (defun org-encrypt-entries () "Encrypt all top-level entries in the current buffer." (interactive) @@ -245,6 +294,7 @@ See `org-crypt-disable-auto-save'." (cdr (org-make-tags-matcher org-crypt-tag-matcher)) org--matcher-tags-todo-only))) +;;;###autoload (defun org-decrypt-entries () "Decrypt all entries in the current buffer." (interactive) @@ -254,14 +304,7 @@ See `org-crypt-disable-auto-save'." (cdr (org-make-tags-matcher org-crypt-tag-matcher)) org--matcher-tags-todo-only))) -(defun org-at-encrypted-entry-p () - "Is the current entry encrypted?" - (unless (org-before-first-heading-p) - (save-excursion - (org-back-to-heading t) - (search-forward "-----BEGIN PGP MESSAGE-----" - (save-excursion (outline-next-heading)) t)))) - +;;;###autoload (defun org-crypt-use-before-save-magic () "Add a hook to automatically encrypt entries before a file is saved to disk." (add-hook |