;; Emacs integration for Enscryerypt ;; Written 2021, 2022 by Markus Triska (triska@metalevel.at) ;; Homepage: https://www.metalevel.at/enscryerypt/ (defvar enscryerypt-scryer "~/scryer-prolog/target/release/scryer-prolog") (defvar enscryerypt-prolog-file (format "%s%s" (if load-in-progress (file-name-directory load-file-name) default-directory) "enscryerypt.pl")) (defvar enscryerypt-prolog-done nil) (defun enscryerypt-start-scryer-prolog () ;; we use the --no-add-history flag to prevent any query history ;; being stored in ~/.scryer_history, including even the fact that ;; any of the Enscryerypt predicates were invoked at all. Note that ;; even without this flag, the interaction with the Prolog process ;; is performed in such a way (using read/1) that passwords do not ;; appear in posted queries, and therefore passwords also do not ;; appear as part of the query history. (let ((process (start-process "scryer-prolog" (current-buffer) enscryerypt-scryer "--no-add-history" enscryerypt-prolog-file))) (setq enscryerypt-prolog-done nil) (set-process-sentinel process 'enscryerypt-sentinel) (enscryerypt-wait-for-prompt process))) ;; Only the sentinel can reliably detect if no more output follows - ;; even if process-status is 'exit, further output can still follow. (defun enscryerypt-sentinel (proc str) (when (string-match "^\\(?:finished\n\\|exited abnormally\\|killed\n\\)" str) (setq enscryerypt-prolog-done t))) (defun enscryerypt-wait-for-done (proc) (while (not enscryerypt-prolog-done) ;; Wait for sentinel indicating done. As `accept-process-output' ;; does not run the sentinel in Emacs <= 23.1, we use `sit-for' to ;; do both. However, `sit-for' returns immediately if keyboard ;; input is available, so we must discard input. (discard-input) (sit-for 0.1))) (defun enscryerypt-encrypted-buffer () (get-buffer-create "enscryerypt-encrypted")) (defun enscryerypt-decrypted-buffer () (get-buffer-create "enscryerypt-decrypted")) (defun enscryerypt-wait-for-prompt (process) (while (not (save-excursion (move-beginning-of-line nil) (looking-at (regexp-quote "?- ")))) (accept-process-output process 0.1)) (erase-buffer)) (defun enscryerypt-show-encrypted () (let ((buf (enscryerypt-encrypted-buffer)) (str (buffer-string))) (with-current-buffer buf (erase-buffer) (save-excursion (insert str))) (switch-to-buffer buf))) (defun enscryerypt-encrypt-file (file) (interactive "fFile: ") (let ((passwd (read-passwd "Password: "))) (with-temp-buffer (message "Encrypting ...") (let ((process (enscryerypt-start-scryer-prolog))) (process-send-string process (format "read(Password), \ encrypt_file_(\"%s\", Password, user_output),halt.\n" (expand-file-name file))) ;; we send the password separately, so that it does not appear ;; in the toplevel history (~/.scryer_history) as part of the query (enscryerypt-send-string-as-hex process passwd) (clear-string passwd) (enscryerypt-wait-for-done process) (enscryerypt-show-encrypted))))) (defun enscryerypt-send-string-as-hex (process str) (process-send-string process (format "\"%s\".\n" (enscryerypt-string-to-hexadecimal str)))) (defun enscryerypt-encrypt-buffer () (interactive) (let ((plaintext (buffer-string)) (buf (enscryerypt-encrypted-buffer)) (passwd (read-passwd "Password: "))) (with-temp-buffer (message "Encrypting ...") (let ((process (let (process-connection-type) ;; use a pipe to avoid overflowing the read buffer if ;; a pty connection is used, see #1080 in Scryer Prolog (enscryerypt-start-scryer-prolog)))) (process-send-string process "read(Password), read(String), \ call_cleanup((phrase_to_stream(encrypt_string_(String, Password), user_output),halt),halt).\n") (enscryerypt-send-string-as-hex process passwd) (clear-string passwd) (enscryerypt-send-string-as-hex process plaintext) (enscryerypt-wait-for-done process) (enscryerypt-show-encrypted))))) (defun enscryerypt-string-to-hexadecimal (string) (let ((bytes (string-to-list (encode-coding-string string buffer-file-coding-system))) hs) (dolist (b bytes) (push (format "\\x%x\\" b) hs)) (apply 'concat (reverse hs)))) ;; (enscryerypt-string-to-hexadecimal "test") (defun enscryerypt-show-decrypted () (let ((buf (enscryerypt-decrypted-buffer)) (plaintext (buffer-string))) (with-current-buffer buf (erase-buffer) (save-excursion (insert plaintext)) (decrypted-mode) (set-buffer-modified-p nil)) (switch-to-buffer buf))) (defun enscryerypt-decrypt-buffer () (interactive) (save-excursion ;; perform a quick plausibility check (encrypted text = 4 facts) (goto-char (point-min)) (unless (= (count-matches "\\.") 4) (error "The buffer content does not have the shape of encrypted output"))) (let ((str (buffer-string)) (buf (enscryerypt-decrypted-buffer)) (passwd (read-passwd "Password: "))) (with-temp-buffer (insert str) (goto-char (point-min)) (while (search-forward "." nil t) (replace-match ",")) (delete-char -1) (setq str (buffer-string)) (erase-buffer) (let ((process (enscryerypt-start-scryer-prolog))) (process-send-string process "read(Password), read(Ps), \ call_cleanup((( decrypt_parameters(Ps, Password) \ ; format(\"**DECRYPTION FAILED!**\", []) \ ),halt),halt).\n") (enscryerypt-send-string-as-hex process passwd) (clear-string passwd) (process-send-string process (format "[%s].\n" str)) (enscryerypt-wait-for-done process) (enscryerypt-show-decrypted))))) (defun enscryerypt-decrypt-file (file) (interactive "fFile: ") (let ((buf (enscryerypt-decrypted-buffer)) (passwd (read-passwd "Password: "))) (with-temp-buffer (message "Decrypting ...") (let ((process (enscryerypt-start-scryer-prolog))) (process-send-string process (format "read(Password), \ call_cleanup((( decrypt_file_(\"%s\", Password) \ ; format(\"**DECRYPTION FAILED!**\", [])),halt),halt).\n" (expand-file-name file))) (enscryerypt-send-string-as-hex process passwd) (clear-string passwd) (enscryerypt-wait-for-done process) (enscryerypt-show-decrypted))))) (defvar decrypted-mode-map (let ((map (make-sparse-keymap))) (suppress-keymap map) (define-key map "q" (lambda () (interactive) (erase-buffer) (kill-buffer))) (define-key map "e" 'fundamental-mode) (define-key map " " 'scroll-up-command) (define-key map (kbd "") 'scroll-down-command) map)) (define-derived-mode decrypted-mode nil "Decrypted" "Mode for decrypted content" (buffer-face-set '(:background "misty rose")))