* Load libraries
Libraries Not from the package manager
#+BEGIN_SRC emacs-lisp
- (dolist (path '("sedit-mouse/" "themes/" "re/"))
+ (dolist (path '("sedit-mouse/" "themes/"))
(add-to-list 'load-path (concat user-emacs-directory path)))
- (dolist (file '(pass-alike eng-paper-theme sedit-mouse))
+ (dolist (file '(eng-paper-theme sedit-mouse))
(require file))
#+END_SRC
* OS specific stuff
(keymap-global-set "C-x C-r" #'re/rename-current-buffer-file)
-(defun re/get-line-from-file (file-path line-number)
- "Return the specified LINE-NUMBER from FILE-PATH as a string.
-Start from bottom if given a negative line number."
- (with-temp-buffer
- (insert-file-contents file-path)
- (if (< line-number 0)
- (progn
- (goto-char (point-max))
- (forward-line line-number))
- (progn
- (goto-char (point-min))
- (forward-line (1- line-number))))
- (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
-
(defun re/get-file-modified-time (filepath)
(interactive)
(format-time-string "%Y-%m-%d-%H%M"
(add-hook 'before-save-hook #'whitespace-cleanup)
(add-hook 'after-save-hook #'executable-make-buffer-file-executable-if-script-p)
#+END_SRC
+* Pass-alike
+** Password generation
+#+BEGIN_SRC emacs-lisp
+(defconst *alphanumerics*
+ '("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m"
+ "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
+ "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M"
+ "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
+ "0" "1" "2" "3" "4" "5" "6" "7" "8" "9")
+ "List of strings of the upper/lower-case English alphabet, and the single-digit numbers.")
+
+(defconst *special-characters*
+ '("!" "?" "$" "%" "&" "'" "(" ")" "*" "+" "," "-" "."
+ "/" ":" ";" "<" "=" ">" "?" "@" "[" "]" "^" "_" "{"
+ "|" "}" "~")
+ "List of strings containing the special visible characters excluding \\ and \" .")
+
+(defun pw-get-char (&optional no-special-chars)
+ "Select a single character at random to be used in a password.
+If `no-special-chars' is t, use only alpha-numeric characters."
+ (let ((selected-charset (append *alphanumerics*
+ (when (null no-special-chars)
+ *special-characters*))))
+ (elt selected-charset (random (length selected-charset)))))
+
+(defun pw-get-chars (password-length &optional no-special-chars)
+ "Generate a list of characters as strings to be made into a password.
+If `no-special-chars' is t, use only alpha-numeric characters."
+ (append (list (pw-get-char no-special-chars))
+ (when (> password-length 1)
+ (pw-get-chars (- password-length 1) no-special-chars))))
+
+(defun generate-password (password-length &optional no-special-chars)
+ "Combine a list of randomly-selected characters into a single string of length given.
+calls `pw-get-chars'. If `no-special-chars' is `t', use only alpha-numeric characters."
+ (apply #'concat (pw-get-chars password-length no-special-chars)))
+
+(defun insert-generated-password (&optional password-length)
+ "Insert a generated password into the current buffer.
+Negative password-length (or negative arg) sets `no-special-chars' for `generate-password'."
+ (interactive "p")
+ (let ((len (if (or (null password-length) (= 1 (abs password-length)))
+ 16
+ (abs password-length)))
+ (no-special-p (and (numberp password-length) (< password-length 0))))
+ (insert (generate-password len no-special-p))))
+#+END_SRC
+** Get a password from a pass(1) entry
+Assume the password is on the first line.
+#+BEGIN_SRC emacs-lisp
+(defun re/get-line-from-file (file-path line-number)
+ "Return the specified LINE-NUMBER from FILE-PATH as a string.
+Start from bottom if given a negative line number."
+ (with-temp-buffer
+ (insert-file-contents file-path)
+ (if (< line-number 0)
+ (progn
+ (goto-char (point-max))
+ (forward-line line-number))
+ (progn
+ (goto-char (point-min))
+ (forward-line (1- line-number))))
+ (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
+
+(defun get-password (&optional filename)
+ "Return a password given a file where it is stored.
+Assumes same structure as `pass(1)'."
+ (re/get-line-from-file (keyfile-string filename) 1))
+
+(defun password-to-clipboard (&optional filename)
+ "Copy the returned password to the clipboard."
+ (interactive)
+ (kill-new (get-password filename)))
+#+END_SRC
+** TOTP
+Generate TOTP tokens, generate the codes, put them on the clipboard. Assumes the format of pass(1), where the TOTP token is the last line in the file.
+#+BEGIN_SRC emacs-lisp
+(dolist (requirement '(bindat gnutls hexl auth-source))
+ (require requirement))
+
+(defun totp--hex-decode-string (string)
+ "Hex-decode STRING and return the result as a unibyte string."
+ (apply #'unibyte-string
+ (seq-map (lambda (s) (hexl-htoi (aref s 0) (aref s 1)))
+ (seq-partition string 2))))
+
+(defun totp (string &optional time digits)
+ "Return a TOTP token using the secret hex STRING and current time.
+TIME is used as counter value instead of current time, if non-nil.
+DIGITS is the number of pin digits and defaults to 6."
+ (let* ((key-bytes (totp--hex-decode-string (upcase string)))
+ (counter (truncate (/ (or time (time-to-seconds)) 30)))
+ (digits (or digits 6))
+ (format-string (format "%%0%dd" digits))
+ ;; we have to manually split the 64 bit number (u64 not supported in Emacs 27.2)
+ (counter-bytes (bindat-pack '((:high u32) (:low u32))
+ `((:high . ,(ash counter -32)) (:low . ,(logand counter #xffffffff)))))
+ (mac (gnutls-hash-mac 'SHA1 key-bytes counter-bytes))
+ (offset (logand (bindat-get-field (bindat-unpack '((:offset u8)) mac 19) :offset) #xf)))
+ (format format-string
+ (mod
+ (logand (bindat-get-field (bindat-unpack '((:totp-pin u32)) mac offset) :totp-pin)
+ #x7fffffff)
+ (expt 10 digits)))))
+
+(defconst base32-alphabet
+ (let ((tbl (make-char-table nil)))
+ (dolist (mapping '(("A" . 0) ("B" . 1) ("C" . 2) ("D" . 3)
+ ("E" . 4) ("F" . 5) ("G" . 6)
+ ("H" . 7) ("I" . 8) ("J" . 9) ("K" . 10)
+ ("L" . 11) ("M" . 12) ("N" . 13)
+ ("O" . 14) ("P" . 15) ("Q" . 16) ("R" . 17)
+ ("S" . 18) ("T" . 19) ("U" . 20)
+ ("V" . 21) ("W" . 22) ("X" . 23) ("Y" . 24)
+ ("Z" . 25) ("2" . 26) ("3" . 27)
+ ("4" . 28) ("5" . 29) ("6" . 30) ("7" . 31)))
+ (aset tbl (string-to-char (car mapping)) (cdr mapping)))
+ tbl)
+ "Base-32 mapping table, as defined in RFC 4648.")
+
+(defun base32-hex-decode (string)
+ "The cheats' version of base-32 decode.
+
+This is not a 100% faithful implementation of RFC 4648. The
+concept of encoding partial quanta is not implemented fully.
+
+No attempt is made to pad the output either as that is not
+required for HMAC-TOTP."
+ (unless (mod (length string) 8)
+ (error "Padding is incorrect"))
+ (setq string (upcase string))
+ (let ((trimmed-array (append (string-trim-right string "=+") nil)))
+ (format "%X" (seq-reduce
+ (lambda (acc char) (+ (ash acc 5) (aref base32-alphabet char)))
+ trimmed-array 0))))
+
+(defun keyfile-string (filename)
+ "Return a string which is the filepath to a `pass(1)' file."
+ (let ((directory "~/.password-store/"))
+ (if (null filename)
+ (read-file-name "Select a password: " directory nil t nil)
+ (concat directory
+ (if (stringp filename)
+ filename
+ (symbol-name filename))
+ ".gpg"))))
+
+(defun get-totp (&optional filename)
+ "Generate a totp code given a file with the secret hex string.
+If no filename given, prompt for one.
+Assumes the file has the string by itself on the last line of the file,
+similar to what pass-otp does, but without the full uri.
+Also assumes the file is in `~/.password-store' and has the `.gpg' extension."
+ (totp (base32-hex-decode (re/get-line-from-file (keyfile-string filename) -1))))
+
+(defun otp-to-clipboard (&optional filename)
+ "Copy the returned totp code to the clipboard."
+ (interactive)
+ (kill-new (get-totp filename)))
+#+END_SRC
+
* Control
#+BEGIN_SRC emacs-lisp
;; Control
+++ /dev/null
-;;; pass-alike.el -*- lexical-binding: t; -*-
-;; This file is not part of GNU Emacs.
-
-;;; Code:
-
-;; Generate totp code
-(dolist (requirement '(bindat gnutls hexl auth-source))
- (require requirement))
-
-(defun totp--hex-decode-string (string)
- "Hex-decode STRING and return the result as a unibyte string."
- (apply #'unibyte-string
- (seq-map (lambda (s) (hexl-htoi (aref s 0) (aref s 1)))
- (seq-partition string 2))))
-
-(defun totp (string &optional time digits)
- "Return a TOTP token using the secret hex STRING and current time.
-TIME is used as counter value instead of current time, if non-nil.
-DIGITS is the number of pin digits and defaults to 6."
- (let* ((key-bytes (totp--hex-decode-string (upcase string)))
- (counter (truncate (/ (or time (time-to-seconds)) 30)))
- (digits (or digits 6))
- (format-string (format "%%0%dd" digits))
- ;; we have to manually split the 64 bit number (u64 not supported in Emacs 27.2)
- (counter-bytes (bindat-pack '((:high u32) (:low u32))
- `((:high . ,(ash counter -32)) (:low . ,(logand counter #xffffffff)))))
- (mac (gnutls-hash-mac 'SHA1 key-bytes counter-bytes))
- (offset (logand (bindat-get-field (bindat-unpack '((:offset u8)) mac 19) :offset) #xf)))
- (format format-string
- (mod
- (logand (bindat-get-field (bindat-unpack '((:totp-pin u32)) mac offset) :totp-pin)
- #x7fffffff)
- (expt 10 digits)))))
-
-(defconst base32-alphabet
- (let ((tbl (make-char-table nil)))
- (dolist (mapping '(("A" . 0) ("B" . 1) ("C" . 2) ("D" . 3)
- ("E" . 4) ("F" . 5) ("G" . 6)
- ("H" . 7) ("I" . 8) ("J" . 9) ("K" . 10)
- ("L" . 11) ("M" . 12) ("N" . 13)
- ("O" . 14) ("P" . 15) ("Q" . 16) ("R" . 17)
- ("S" . 18) ("T" . 19) ("U" . 20)
- ("V" . 21) ("W" . 22) ("X" . 23) ("Y" . 24)
- ("Z" . 25) ("2" . 26) ("3" . 27)
- ("4" . 28) ("5" . 29) ("6" . 30) ("7" . 31)))
- (aset tbl (string-to-char (car mapping)) (cdr mapping)))
- tbl)
- "Base-32 mapping table, as defined in RFC 4648.")
-
-(defun base32-hex-decode (string)
- "The cheats' version of base-32 decode.
-
-This is not a 100% faithful implementation of RFC 4648. The
-concept of encoding partial quanta is not implemented fully.
-
-No attempt is made to pad the output either as that is not
-required for HMAC-TOTP."
- (unless (mod (length string) 8)
- (error "Padding is incorrect"))
- (setq string (upcase string))
- (let ((trimmed-array (append (string-trim-right string "=+") nil)))
- (format "%X" (seq-reduce
- (lambda (acc char) (+ (ash acc 5) (aref base32-alphabet char)))
- trimmed-array 0))))
-
-(defun keyfile-string (filename)
- "Return a string which is the filepath to a `pass(1)' file."
- (let ((directory "~/.password-store/"))
- (if (null filename)
- (read-file-name "Select a password: " directory nil t nil)
- (concat directory
- (if (stringp filename)
- filename
- (symbol-name filename))
- ".gpg"))))
-
-(defun get-totp (&optional filename)
- "Generate a totp code given a file with the secret hex string.
-If no filename given, prompt for one.
-Assumes the file has the string by itself on the last line of the file,
-similar to what pass-otp does, but without the full uri.
-Also assumes the file is in `~/.password-store' and has the `.gpg' extension."
- (totp (base32-hex-decode (re/get-line-from-file (keyfile-string filename) -1))))
-
-(defun otp-to-clipboard (&optional filename)
- "Copy the returned totp code to the clipboard."
- (interactive)
- (kill-new (get-totp filename)))
-\f
-;; Find password from pass(1) entry
-
-(defun get-password (&optional filename)
- "Return a password given a file where it is stored.
-Assumes same structure as `pass(1)'."
- (re/get-line-from-file (keyfile-string filename) 1))
-
-(defun password-to-clipboard (&optional filename)
- "Copy the returned password to the clipboard."
- (interactive)
- (kill-new (get-password filename)))
-\f
-;; Password generation
-
-(defconst *alphanumerics*
- '("a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m"
- "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
- "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M"
- "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
- "0" "1" "2" "3" "4" "5" "6" "7" "8" "9")
- "List of strings of the upper/lower-case English alphabet, and the single-digit numbers.")
-
-(defconst *special-characters*
- '("!" "?" "$" "%" "&" "'" "(" ")" "*" "+" "," "-" "."
- "/" ":" ";" "<" "=" ">" "?" "@" "[" "]" "^" "_" "{"
- "|" "}" "~")
- "List of strings containing the special visible characters excluding \\ and \" .")
-
-(defun pw-get-char (&optional no-special-chars)
- "Select a single character at random to be used in a password.
-If `no-special-chars' is t, use only alpha-numeric characters."
- (let ((selected-charset (append *alphanumerics*
- (when (null no-special-chars)
- *special-characters*))))
- (elt selected-charset (random (length selected-charset)))))
-
-(defun pw-get-chars (password-length &optional no-special-chars)
- "Generate a list of characters as strings to be made into a password.
-If `no-special-chars' is t, use only alpha-numeric characters."
- (append (list (pw-get-char no-special-chars))
- (when (> password-length 1)
- (pw-get-chars (- password-length 1) no-special-chars))))
-
-(defun generate-password (password-length &optional no-special-chars)
- "Combine a list of randomly-selected characters into a single string of length given.
-calls `pw-get-chars'. If `no-special-chars' is `t', use only alpha-numeric characters."
- (apply #'concat (pw-get-chars password-length no-special-chars)))
-
-(defun insert-generated-password (&optional password-length)
- "Insert a generated password into the current buffer.
-Negative password-length (or negative arg) sets `no-special-chars' for `generate-password'."
- (interactive "p")
- (let ((len (if (or (null password-length) (= 1 (abs password-length)))
- 16
- (abs password-length)))
- (no-special-p (and (numberp password-length) (< password-length 0))))
- (insert (generate-password len no-special-p))))
-
-(provide 'pass-alike)
-;;; pass-alike.el ends here