do not edit — generated by btf.
git.druid.rocksindexdruid520radiumradium-sign.el

radium-sign.el


;;; radium-sign.el, sign/verify the current file: ssh or signify -*- lexical-binding: t; -*-
;;
;; item 11: "which keys configurable/asked" - the METHOD (ssh vs
;; signify) is a defcustom, set once, not asked every time (whichever
;; of the two is actually installed wins if only one is); the KEY is
;; asked every time via completing-read, since which key to sign with
;; is exactly the kind of per-use decision a default would get wrong
;; half the time. output is always FILE.sig next to FILE, the same
;; sigfile convention both tools and git's own ssh-signing already use.
;;
;; ssh signing (openssh >= 8.2, `ssh-keygen -Y sign/verify') needs a
;; NAMESPACE - openssh's own manpage example for signing an arbitrary
;; file (not a git object, not an email) uses "file", so that's the
;; default here too (radium-sign-ssh-namespace, still a real defcustom
;; if something else is ever wanted). candidate keys are ~/.ssh/*.pub:
;; `ssh-keygen -Y sign -f pubkey' signs through ssh-agent when the
;; matching private key is loaded there rather than needing the
;; private key file itself touched by this elisp at all - the safer of
;; the two ways ssh-keygen -Y sign can be given a key.
;;
;; signify has no equivalent agent-mediated mode - -s wants the real
;; secret key file - so candidates come from radium-sign-signify-key-dir
;; (~/.signify by default, configurable: signify itself has no fixed
;; per-user convention the way ssh has ~/.ssh).
 
(defgroup radium nil
  "this config's own settings - one shared group so `M-x customize-group
radium' finds all of them, rather than each defcustom scattered under
whichever built-in group happens to be nearby."
  :group 'emacs)
 
(defcustom radium-sign-method 'ssh
  "which tool C-c v S/V use: `ssh' or `signify'. if the one this is set
to isn't actually installed, radium-sign falls back to whichever one
is - and only errors if neither is."
  :type '(choice (const ssh) (const signify))
  :group 'radium)
 
(defcustom radium-sign-ssh-namespace "file"
  "the -n namespace `ssh-keygen -Y sign/verify' signs/checks under -
openssh's own manpage example for signing a plain file, not a git
object or an email, uses exactly this."
  :type 'string
  :group 'radium)
 
(defcustom radium-sign-signify-key-dir "~/.signify"
  "where C-c v S/V look for signify *.sec/*.pub keys - signify itself
has no fixed convention for this the way ssh has ~/.ssh."
  :type 'directory
  :group 'radium)
 
(defun radium-sign--available-methods ()
  (delq nil (list (and (executable-find "ssh-keygen") 'ssh)
                   (and (executable-find "signify") 'signify))))
 
(defun radium-sign--method ()
  "radium-sign-method if it's actually installed, else whichever
available method there is, else a real error - never a silent no-op."
  (let ((avail (radium-sign--available-methods)))
    (cond
     ((memq radium-sign-method avail) radium-sign-method)
     (avail (car avail))
     (t (user-error "radium-sign: neither ssh-keygen nor signify is installed")))))
 
(defun radium-sign--ssh-keys ()
  (file-expand-wildcards (expand-file-name "*.pub" "~/.ssh")))
 
(defun radium-sign--signify-keys (ext)
  (file-expand-wildcards (expand-file-name (concat "*." ext) radium-sign-signify-key-dir)))
 
(defun radium-sign--read-key (candidates prompt)
  "completing-read over CANDIDATES if there are any, else a real
read-file-name - never assume the file i actually want is in the
list, just offer what's there to complete against."
  (if candidates
      (completing-read prompt candidates nil nil)
    (read-file-name prompt)))
 
(defun radium-sign-file ()
  "sign the file this buffer visits (C-c v S): ssh-keygen -Y sign or
signify -S, whichever radium-sign--method resolves to, with the key
asked every time. writes FILE.sig next to FILE."
  (interactive)
  (let* ((file (or buffer-file-name (user-error "radium-sign: buffer has no file")))
         (method (radium-sign--method)))
    (pcase method
      ('ssh
       (let* ((key (radium-sign--read-key (radium-sign--ssh-keys) "sign with key: "))
              (buf (get-buffer-create "*radium-sign*")))
         (if (zerop (call-process "ssh-keygen" nil buf nil
                                   "-Y" "sign" "-f" key "-n" radium-sign-ssh-namespace file))
             (message "radium-sign: signed %s -> %s.sig" (file-name-nondirectory file) (file-name-nondirectory file))
           (display-buffer buf)
           (user-error "radium-sign: ssh-keygen failed, see *radium-sign*"))))
      ('signify
       (let* ((key (radium-sign--read-key (radium-sign--signify-keys "sec") "sign with key: "))
              (sig (concat file ".sig"))
              (buf (get-buffer-create "*radium-sign*")))
         (if (zerop (call-process "signify" nil buf nil
                                   "-S" "-s" key "-m" file "-x" sig))
             (message "radium-sign: signed %s -> %s" (file-name-nondirectory file) (file-name-nondirectory sig))
           (display-buffer buf)
           (user-error "radium-sign: signify failed, see *radium-sign*")))))))
 
(defun radium-verify-file ()
  "verify FILE.sig against the file this buffer visits (C-c v V),
same method/key-prompt shape as radium-sign-file."
  (interactive)
  (let* ((file (or buffer-file-name (user-error "radium-verify: buffer has no file")))
         (sig (concat file ".sig"))
         (method (radium-sign--method)))
    (unless (file-exists-p sig)
      (user-error "radium-verify: no %s" sig))
    (pcase method
      ('ssh
       ;; -Y verify has no "here's the public key" flag at all - only
       ;; -f ALLOWED-SIGNERS-FILE (an authorized_keys-shaped file:
       ;; "identity [namespaces=\"ns\"] keytype keydata") plus -I to
       ;; name which identity in it to check against, and the message
       ;; itself is read from stdin, not a flag - confirmed directly
       ;; against a real generated key/signature pair, not assumed
       ;; from the sign side's own flags.
       (let* ((key (radium-sign--read-key (radium-sign--ssh-keys) "verify against key: "))
              (allowed (make-temp-file "radium-sign-allowed"))
              (buf (get-buffer-create "*radium-sign*")))
         (unwind-protect
             (progn
               (with-temp-file allowed
                 (insert "radium-sign " (with-temp-buffer
                                          (insert-file-contents key)
                                          (string-trim (buffer-string))) "\n"))
               (if (zerop (call-process "ssh-keygen" file buf nil
                                         "-Y" "verify" "-f" allowed "-I" "radium-sign"
                                         "-n" radium-sign-ssh-namespace "-s" sig))
                   (message "radium-verify: %s: signature OK" (file-name-nondirectory file))
                 (display-buffer buf)
                 (user-error "radium-verify: signature check FAILED, see *radium-sign*")))
           (delete-file allowed))))
      ('signify
       (let* ((key (radium-sign--read-key (radium-sign--signify-keys "pub") "verify against key: "))
              (buf (get-buffer-create "*radium-sign*")))
         (if (zerop (call-process "signify" nil buf nil
                                   "-V" "-p" key "-m" file "-x" sig))
             (message "radium-verify: %s: signature OK" (file-name-nondirectory file))
           (display-buffer buf)
           (user-error "radium-verify: signature check FAILED, see *radium-sign*")))))))
 
(global-set-key (kbd "C-c v S") #'radium-sign-file)
(global-set-key (kbd "C-c v V") #'radium-verify-file)
 
(provide 'radium-sign)
powered by btf.