#+title:  Personal Password Generator
#+author: Howard X. Abrams
#+date:   2021-01-11
#+tags: emacs

A literate programming version for Emacs code to generate and store passwords.

#+begin_src emacs-lisp :exports none
  ;;; ha-passwords --- Emacs code to generate and store passwords. -*- lexical-binding: t; -*-
  ;;
  ;; © 2021-2023 Howard X. Abrams
  ;;   Licensed under a Creative Commons Attribution 4.0 International License.
  ;;   See http://creativecommons.org/licenses/by/4.0/
  ;;
  ;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
  ;; Maintainer: Howard X. Abrams
  ;; Created: January 11, 2021
  ;;
  ;; This file is not part of GNU Emacs.
  ;;
  ;; *NB:* Do not edit this file. Instead, edit the original literate file at:
  ;;            ~/src/hamacs/ha-passwords.org
  ;;       And tangle the file to recreate this one.
  ;;
  ;;; Code:
#+end_src
* Overlay Redaction
Love [[https://xenodium.com/redact-that-buffer/][this idea]] for using a regular expression to /redact/ (or at least, obscure) sensitive information before screen sharing.
#+begin_src emacs-lisp
  (defun toggle-redact-buffer (regexp)
    "Redact buffer content matching regexp. A space redacts all."
    (interactive (list (read-regexp "Text to Redact (Regexp)" 'regexp-history-last)))
    (let* ((redacted)
           (matches (let ((results '()))
                      (when (string-empty-p regexp)
                        (setq regexp "[[:graph:]]")
                        (setq regexp-history-last regexp)
                        (add-to-history 'regexp-history regexp))
                      (save-excursion
                        (goto-char (point-min))
                        (while (re-search-forward regexp nil t)
                          (push (cons (match-beginning 0) (match-end 0)) results)))
                      (nreverse results))))
      (mapc (lambda (match)
              (dolist (overlay (overlays-in (car match) (cdr match)))
                (setq redacted t)
                (delete-overlay overlay))
              (unless redacted
                (overlay-put (make-overlay (car match) (cdr match))
                             'display (make-string (- (cdr match) (car match)) ?x))))
            matches)))
#+end_src
I’m not sure how often I will use this, so I’m not putting it on a keybinding, also so, I will also not put a name-spacing prefix on the function.
* Password Generation
Let's assume that I store a bunch of words in data files:
#+begin_src emacs-lisp
  (defvar ha-passwords-data-files (list (expand-file-name "adjectives.txt"
                                                          (expand-file-name "data" hamacs-source-dir))
                                        (expand-file-name "colors.txt"
                                                          (expand-file-name "data" hamacs-source-dir))
                                        (expand-file-name "nouns.txt"
                                                          (expand-file-name "data" hamacs-source-dir)))
    "List of file name containing a data lines for our password generator. Order of these files matter.")

  (defvar ha-passwords-data nil
    "Contains a list of lists of words that we can choose.")
#+end_src

You can see where I'm going with this, can't you? Let's read them into list variables.
#+begin_src emacs-lisp
  (defun ha-passwords--read-data-file (filename)
    (with-temp-buffer
      (insert-file-contents filename)
      (split-string (buffer-string) "\n" t)))

#+end_src

Now we get three or so words from our list of lists:
#+begin_src emacs-lisp
  (defun ha-passwords-words ()
    (unless ha-passwords-data
      (setq ha-passwords-data
            (--map (ha-passwords--read-data-file it) ha-passwords-data-files)))

    (--map (nth (random (length it)) it) ha-passwords-data))
#+end_src

Let's make a password:

#+begin_src emacs-lisp
  (defun ha-passwords-generate (&optional separator)
    (unless separator
      (setq separator "-"))

    (let* ((choices '("!" "@" "#" "$" "%" "^" "&" "*"))
           (choice (random (length choices)))
           (number (1+ choice)))
      (->> (ha-passwords-words)
           (s-join separator)
           (s-capitalize)
           (s-append (nth choice choices))
           (s-append (number-to-string number)))))
#+end_src

#+begin_src emacs-lisp
  (defun generate-password (&optional separator)
    (interactive)
    (let ((passphrase (ha-passwords-generate separator)))
      (kill-new passphrase)
      (message "Random password: %s" passphrase)))
#+end_src
* Keybindings
Got make it easy to call:
#+begin_src emacs-lisp
  (ha-leader "a g" '("generate passwd" . generate-password))
#+end_src
* Technical Artifacts :noexport:
This will =provide= a code name, so that we can =require= this.

#+begin_src emacs-lisp :exports none
  (provide 'ha-passwords)
  ;;; ha-passwords.el ends here
#+end_src

#+description: A literate programming version for Emacs code to generate and store passwords.

#+property:    header-args:sh :tangle no
#+property:    header-args:emacs-lisp :tangle yes
#+property:    header-args    :results none :eval no-export :comments no mkdirp yes

#+options:     num:nil toc:t todo:nil tasks:nil tags:nil date:nil
#+options:     skip:nil author:nil email:nil creator:nil timestamp:nil
#+infojs_opt:  view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
