From: ueno Date: Wed, 12 Apr 2006 02:45:25 +0000 (+0000) Subject: Added README and epg-file.el. X-Git-Tag: epgsm-branchpoint~139 X-Git-Url: http://git.chise.org/gitweb/?a=commitdiff_plain;h=31d97193bafb50321267222e8e7db2489c4c43b0;p=elisp%2Fepg.git Added README and epg-file.el. --- diff --git a/README b/README new file mode 100644 index 0000000..187db0d --- /dev/null +++ b/README @@ -0,0 +1,30 @@ +* What's this? + +EasyPG is yet another GnuPG interface for Emacs. It consists of a +transparent file encryption application and easy-to-use elisp library +to interact with GnuPG. + +* Quick start + +(0) Put (require 'epg-file) in your ~/.emacs.el + +(1) Restart emacs + +(2) C-x C-f ~/test.gpg + +* Advantages over other competitors + +** EasyPG avoides potential security flaws of Emacs. + +*** `call-process-region' writes data in region to a temporary file. +PGG and gpg.el use `call-process-region' to communicate with a +subprocess "gpg". So, your passphrases may leak to the filesystem. + +*** There is no way to clear strings safely. +To prevent passphrases from been stealing from cores, `read-passwd' +function clears passphrase strings by `(fillarray string 0)'. +However, it is not enough. In GC sweep phase, Emacs does compaction +on small strings. If GC happens before `fillarray', passphrase +strings may be copied elsewhere in the memory. + +PGG and gpg.el enables passphrase caching by default. diff --git a/epg-file.el b/epg-file.el new file mode 100644 index 0000000..19cda1c --- /dev/null +++ b/epg-file.el @@ -0,0 +1,225 @@ +(require 'epg) + +(defcustom epg-file-name-regexp "\\.gpg\\'" + "Regexp that matches filenames that are assumed to be encrypted +with GnuPG." + :type 'regexp + :group 'epg-file) + +(defun epg-file-handler (operation &rest args) + (let ((epg-file-operation (get operation 'epg-file))) + (if epg-file-operation + (apply epg-file-operation args) + (epg-file-invoke-default-handler operation args)))) + +(defun epg-file-invoke-default-handler (operation args) + (let ((inhibit-file-name-handlers + (cons 'epg-file-handler + (and (eq inhibit-file-name-operation operation) + inhibit-file-name-handlers))) + (inhibit-file-name-operation operation)) + (apply operation args))) + +(defun epg-file-write-region (start end filename &optional append visit + lockname mustbenew) + (let* ((visit-file (if (stringp visit) + (expand-file-name visit) + (expand-file-name filename))) + ;; XXX: Obtain the value returned by choose_write_coding_system + (coding-system (condition-case nil + (epg-file-invoke-default-handler + #'write-region + (list start end "/")) + (file-error last-coding-system-used))) + ;; start and end are normally buffer positions + ;; specifying the part of the buffer to write. + ;; If start is nil, that means to use the entire buffer contents. + ;; If start is a string, then output that string to the file + ;; instead of any buffer contents; end is ignored. + (string (encode-coding-string (cond + ((stringp start) + start) + ((null start) + (buffer-string)) + (t + (buffer-substring start end))) + coding-system))) + (with-temp-buffer + (set-buffer-multibyte nil) + ;; Optional fourth argument append if non-nil means + ;; append to existing file contents (if any). If it is an integer, + ;; seek to that offset in the file before writing. + (if (and append (file-exists-p filename)) + ;; Enable passphrase cache on this temp buffer + (let ((coding-system-for-read 'binary)) + ;; set visit to t so that passphrase is cached + (insert-file-contents filename t) + (setq buffer-file-name nil))) + ;; Insert data to encrypt + (goto-char (if (integerp append) (1+ append) (point-max))) + (delete-region (point) (min (+ (point) (length string)) (point-max))) + (insert string) + + (let ((coding-system-for-write 'binary) + (coding-system-for-read 'binary) + (context (epg-make-context)) + cipher) + (when (setq cipher (epg-encrypt-string context (buffer-string) nil)) + (if (memq system-type '(ms-dos windows-nt)) + (setq buffer-file-type t)) + (epg-file-invoke-default-handler + #'write-region + (list cipher nil filename nil 'not-visit lockname mustbenew))))) + ;; Optional fifth argument visit, if t or a string, means + ;; set the last-save-file-modtime of buffer to this file's modtime + ;; and mark buffer not modified. + ;; If visit is a string, it is a second file name; + ;; the output goes to filename, but the buffer is marked as visiting visit. + ;; visit is also the file name to lock and unlock for clash detection. + ;; If visit is neither t nor nil nor a string, + ;; that means do not display the "Wrote file" message. + (when (or (eq visit t) (stringp visit)) + (setq buffer-file-name filename) + (set-visited-file-modtime)) + (when (stringp visit) + (setq buffer-file-name visit)) + (when (or (eq visit t) (eq visit nil) (stringp visit)) + (message "Wrote %s" visit-file)) + (setq last-coding-system-used coding-system) + nil)) + +(defun epg-file-insert-file-contents (filename &optional visit beg end replace) + (barf-if-buffer-read-only) + + ;; If visit is non-nil, beg and end must be nil. + (if (and visit (or beg end)) + (error "Attempt to visit less than an entire file")) + + (let ((expanded-filename (expand-file-name filename)) + (length 0)) + (if (file-exists-p expanded-filename) + (let* ((local-copy (epg-file-invoke-default-handler + 'file-local-copy + (list expanded-filename))) + (local-file (or local-copy expanded-filename)) + (coding-system-for-read 'binary) + (context (epg-make-context)) + string) + (unwind-protect + (progn + (setq string (epg-decrypt-file context local-file) + length (length string)) + (if replace + (goto-char (point-min))) + (save-excursion + (let ((buffer-file-name (if visit nil buffer-file-name))) + (save-restriction + (narrow-to-region (point) (point)) + (insert string) + (epg-file-decode-coding-inserted-region + (point-min) (point-max) + expanded-filename + visit beg end replace)) + (if replace + (delete-region (point) (point-max)))))) + (when (and local-copy (file-exists-p local-copy)) + (delete-file local-copy))))) + ;; If second argument visit is non-nil, the buffer's visited filename + ;; and last save file modtime are set, and it is marked unmodified. + (when visit + (unlock-buffer) + (setq buffer-file-name expanded-filename) + (set-visited-file-modtime)) + + ;; If visiting and the file does not exist, visiting is completed + ;; before the error is signaled. + (if (and visit (not (file-exists-p expanded-filename))) + (signal 'file-error (list "Opening input file" filename))) + + ;; Returns list of absolute file name and number of characters inserted. + (list expanded-filename length))) + +(if (fboundp 'decode-coding-inserted-region) + (defalias 'epg-file-decode-coding-inserted-region 'decode-coding-inserted-region) + (defun epg-file-decode-coding-inserted-region (from to filename + &optional visit beg end replace) + "Decode the region between FROM and TO as if it is read from file FILENAME. +The idea is that the text between FROM and TO was just inserted somehow. +Optional arguments VISIT, BEG, END, and REPLACE are the same as those +of the function `insert-file-contents'. +Part of the job of this function is setting `buffer-undo-list' appropriately." + (save-excursion + (save-restriction + (let ((coding coding-system-for-read) + undo-list-saved) + (if visit + ;; Temporarily turn off undo recording, if we're decoding the + ;; text of a visited file. + (setq buffer-undo-list t) + ;; Otherwise, if we can recognize the undo elt for the insertion, + ;; remove it and get ready to replace it later. + ;; In the mean time, turn off undo recording. + (let ((last (car-safe buffer-undo-list))) + (if (and (consp last) (eql (car last) from) (eql (cdr last) to)) + (setq undo-list-saved (cdr buffer-undo-list) + buffer-undo-list t)))) + (narrow-to-region from to) + (goto-char (point-min)) + (or coding + (setq coding (funcall set-auto-coding-function + filename (- (point-max) (point-min))))) + (or coding + (setq coding (car (find-operation-coding-system + 'insert-file-contents + filename visit beg end replace)))) + (if (coding-system-p coding) + (or enable-multibyte-characters + (setq coding + (coding-system-change-text-conversion coding 'raw-text))) + (setq coding nil)) + (if coding + (decode-coding-region (point-min) (point-max) coding) + (setq last-coding-system-used coding)) + ;; If we're decoding the text of a visited file, + ;; the undo list should start out empty. + (if visit + (setq buffer-undo-list nil) + ;; If we decided to replace the undo entry for the insertion, + ;; do so now. + (if undo-list-saved + (setq buffer-undo-list + (cons (cons from (point-max)) undo-list-saved))))))))) + +(put 'epg-file-handler 'safe-magic t) +(let (epg-file-operations) + (mapc + (lambda (operation) + (let ((epg-file-operation (intern (format "epg-file-%s" operation)))) + (when (fboundp epg-file-operation) + (push operation epg-file-operations) + (put operation 'epg-file epg-file-operation)))) + '(access-file add-name-to-file byte-compiler-base-file-name copy-file + delete-directory delete-file diff-latest-backup-file directory-file-name + directory-files directory-files-and-attributes dired-call-process + dired-compress-file dired-uncache expand-file-name + file-accessible-directory-p file-attributes file-directory-p + file-executable-p file-exists-p file-local-copy file-remote-p file-modes + file-name-all-completions file-name-as-directory file-name-completion + file-name-directory file-name-nondirectory file-name-sans-versions + file-newer-than-file-p file-ownership-preserved-p file-readable-p + file-regular-p file-symlink-p file-truename file-writable-p + find-backup-file-name find-file-noselect get-file-buffer + insert-directory insert-file-contents load make-directory + make-directory-internal make-symbolic-link rename-file set-file-modes + set-visited-file-modtime shell-command substitute-in-file-name + unhandled-file-name-directory vc-registered verify-visited-file-modtime + write-region)) + (put 'epg-file-handler 'operations epg-file-operations)) + +(unless (assoc epg-file-name-regexp file-name-handler-alist) + (push (cons epg-file-name-regexp 'epg-file-handler) + file-name-handler-alist)) + +(unless (assoc epg-file-name-regexp auto-mode-alist) + (push (list epg-file-name-regexp nil 'strip-suffix) + auto-mode-alist))