branch: elpa/age
commit 29a74552a900959b72f013da8c527f3497ff27a4
Author: Bas Alberts <[email protected]>
Commit: Bas Alberts <[email protected]>

    more intuitive handling of passphrase encrypted age files
    
    closes https://github.com/anticomputer/age.el/issues/3
---
 age.el | 112 ++++++++++++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 73 insertions(+), 39 deletions(-)

diff --git a/age.el b/age.el
index eb848ca2386..ff9eee092ff 100644
--- a/age.el
+++ b/age.el
@@ -1,4 +1,4 @@
-;;; age.el --- the Age Library -*- lexical-binding: t -*-
+;;; age.el --- The Age Encryption Library -*- lexical-binding: t -*-
 
 ;; EPG/EPA modified to work with Age: https://github.com/FiloSottile/age
 
@@ -298,6 +298,7 @@ or higher is installed."
   protocol
   program
   armor
+  passphrase
   (passphrase-callback (list #'age-passphrase-callback-function))
   edit-callback
   process
@@ -329,6 +330,11 @@ or higher is installed."
   (declare (obsolete setf "25.1"))
   (setf (age-context-armor context) armor))
 
+(defun age-context-set-passphrase (context passphrase)
+  "Specify if the file is in PASSPHRASE mode."
+  (declare (obsolete setf "25.1"))
+  (setf (age-context-passphrase context) passphrase))
+
 ;; XXX: unused currently, so... untested.
 (defun age-context-set-passphrase-callback (context
                                            passphrase-callback)
@@ -347,6 +353,30 @@ question, and the callback data (if any)."
 
 ;;; Functions
 
+(defun age-scrypt-p (file)
+  "Check for passphrase scrypt stanza in age FILE."
+  (with-temp-buffer
+    ;; disable age file handling for this insert, we just want to grab a header
+    (let ((file-name-handler-alist (remq age-file-handler 
file-name-handler-alist))
+          (auto-mode-alist (remq age-file-auto-mode-alist-entry 
auto-mode-alist)))
+      (insert-file-contents-literally file nil 0 100))
+    (let ((lines
+           ;; grab the first two lines
+           (cl-loop repeat 2
+                    unless (eobp)
+                    collect
+                    (prog1 (buffer-substring-no-properties
+                            (line-beginning-position)
+                            (line-end-position))
+                      (forward-line 1)))))
+      ;; if the first line is the ascii armor marker, base64 decode the second 
line
+      (let ((b64 (string-match-p
+                  "-----BEGIN AGE ENCRYPTED FILE-----" (car lines)))
+            (l2 (cadr lines)))
+        ;; if the second line contains the scrypt stanza, it is a passphrase 
file
+        (when (string-match-p "-> scrypt " (if b64 (base64-decode-string l2) 
l2))
+          t)))))
+
 (defun age-context-result-for (context name)
   "Return the result of CONTEXT associated with NAME."
   (cdr (assq name (age-context-result context))))
@@ -567,7 +597,7 @@ If you are unsure, use synchronous version of this function
   (setf (age-context-result context) nil)
   (let ((identity
          ;; only nag if we're not in passphrase mode
-         (when age-default-identity
+         (unless (or (age-context-passphrase context) (not 
age-default-identity))
            (if (or age-always-use-default-keys
                    (y-or-n-p "Use default identity? "))
                age-default-identity
@@ -575,17 +605,17 @@ If you are unsure, use synchronous version of this 
function
     (age--start context
                 (append '("--decrypt")
                         ;; identity may be a list of identities, skip in 
passphrase mode
-                        (if age-default-identity
-                            (if (listp identity)
-                                (apply #'nconc
-                                      (mapcar
-                                       (lambda (id)
-                                          (when age-debug
-                                            (message "Adding id: %s" id))
-                                          (when (file-exists-p 
(expand-file-name id))
-                                            (list "-i" (expand-file-name id))))
-                                       identity))
-                              (list "-i" (expand-file-name identity))))
+                        (unless (or (age-context-passphrase context) (not 
age-default-identity))
+                          (if (listp identity)
+                              (apply #'nconc
+                                    (mapcar
+                                     (lambda (id)
+                                        (when age-debug
+                                          (message "Adding id: %s" id))
+                                        (when (file-exists-p (expand-file-name 
id))
+                                          (list "-i" (expand-file-name id))))
+                                     identity))
+                            (list "-i" (expand-file-name identity))))
                         (list "--" (age-data-file cipher))))))
 
 (defun age--check-error-for-decrypt (context)
@@ -640,38 +670,40 @@ If you are unsure, use synchronous version of this 
function
 `age-encrypt-file' or `age-encrypt-string' instead."
   (setf (age-context-operation context) 'encrypt)
   (setf (age-context-result context) nil)
-  ;; XXX: fixme ... we _ALWAYS_ need recipients
   (let ((recipients
          ;; ... unless we're in passphrase mode :P
-         (when age-default-recipient
+         (unless (or (age-context-passphrase context) (not 
age-default-recipient))
            (or recipients
                (age-select-keys
                 context
                 "Select recipients for encryption.")))))
-    (age--start context
-                ;; if recipients is nil, we go to the default identity
-               (append '("--encrypt")
-                        ;; only add recipients if we're not in passphrase mode
-                        (if age-default-recipient
-                           (apply #'nconc
-                                  (mapcar
-                                   (lambda (recipient)
-                                      ;; recipients is a list of age public 
keys
-                                      (when age-debug
-                                        (message "Adding recipient: %s" 
recipient))
-                                      (if (file-exists-p (expand-file-name 
recipient))
-                                          (progn
-                                            (when age-debug
-                                              (message "Adding file based 
recipient(s)."))
-                                            (list "-R" (expand-file-name 
recipient)))
-                                        (when age-debug
-                                          (message "Adding string based 
recipient."))
-                                       (list "-r" recipient)))
-                                   recipients))
-                          ;; passphrase mode, requires rage for pinentry 
support
-                          (list "-p"))
-                       (if (age-data-file plain)
-                           (list "--" (age-data-file plain))))))
+    (age--start
+     context
+     ;; if recipients is nil, we go to the default identity
+     (append
+      '("--encrypt")
+      ;; only add recipients if we're not in passphrase mode
+      (if (or (age-context-passphrase context) (not age-default-recipient))
+          ;; passphrase mode, requires rage for pinentry support
+          (list "-p")
+        ;; recipient mode
+       (apply #'nconc
+              (mapcar
+               (lambda (recipient)
+                  ;; recipients is a list of age public keys
+                  (when age-debug
+                    (message "Adding recipient: %s" recipient))
+                  (if (file-exists-p (expand-file-name recipient))
+                      (progn
+                        (when age-debug
+                          (message "Adding file based recipient(s)."))
+                        (list "-R" (expand-file-name recipient)))
+                    (when age-debug
+                      (message "Adding string based recipient."))
+                   (list "-r" recipient)))
+               recipients)))
+      (if (age-data-file plain)
+         (list "--" (age-data-file plain))))))
   (when (age-data-string plain)
     (if (eq (process-status (age-context-process context)) 'run)
        (process-send-string (age-context-process context)
@@ -979,6 +1011,7 @@ encryption is used."
         string length entry)
     (if visit
        (setq buffer-file-name file))
+    (age-context-set-passphrase context (age-scrypt-p file))
     (age-context-set-passphrase-callback
      context
      (cons #'age-file-passphrase-callback-function
@@ -1115,6 +1148,7 @@ encryption is used."
           ((listp age-file-encrypt-to) age-file-encrypt-to)
           ((stringp age-file-encrypt-to) (list age-file-encrypt-to))))
         buffer)
+    (age-context-set-passphrase context (age-scrypt-p file))
     (age-context-set-passphrase-callback
      context
      (cons #'age-file-passphrase-callback-function

Reply via email to