Subject: 30.0.50; Toggle password visibility

From: Michael Albinus <[email-masked]>
Date: Fri, 16 Feb 2024 17:47:20 +0100

`read-passwd' reads a password with hidden input from the
minibuffer. However, passord input fields in other applications but
Emacs allow to toggle the visibility of input while typing the password.

The appended patch tries to implement this for Emacs. While reading the
password, it binds TAB in the minibuffer to a function which toggles the
password visibility.

With graphical displays, it adds also an indicator to the mode line for
password visibility. Clicking this indicator with mouse-1 toggles the
password visibility in the minibuffer.

Comments?

Best regards, Michael.

In GNU Emacs 30.0.50 (build 18, x86_64-pc-linux-gnu, GTK+ Version
 3.24.41, cairo version 1.18.0) of 2024-02-16 built on gandalf
Repository revision: 572d58b5e8d0f1f1244b9ccab8f02c4f50ca8d12
Repository branch: master
Windowing system distributor 'The X.Org Foundation', version 11.0.12302004
System Description: Fedora Linux 39 (Workstation Edition)

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG
LIBOTF LIBSELINUX LIBXML2 M17N_FLT MODULES NATIVE_COMP NOTIFY INOTIFY
PDUMPER PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS
TREE_SITTER X11 XDBE XIM XINPUT2 XPM GTK3 ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  value of $XMODIFIERS: @im=ibus
  locale-coding-system: utf-8

Major mode: Group

Minor modes in effect:
  gnus-undo-mode: t
  display-time-mode: t
  delete-selection-mode: t
  icomplete-mode: t
  global-goto-address-mode: t
  goto-address-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  minibuffer-regexp-mode: t
  buffer-read-only: t
  column-number-mode: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
/home/albinus/src/elpa/packages/debbugs/debbugs hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs
/home/albinus/src/elpa/packages/debbugs/debbugs-org hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-org
/home/albinus/src/elpa/packages/debbugs/debbugs-gnu hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-gnu
/home/albinus/src/elpa/packages/debbugs/debbugs-guix hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-guix
/home/albinus/src/elpa/packages/debbugs/debbugs-browse hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-browse
/home/albinus/src/elpa/packages/debbugs/debbugs-pkg hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-pkg
/home/albinus/src/elpa/packages/debbugs/debbugs-autoloads hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-autoloads
/home/albinus/src/elpa/packages/debbugs/debbugs-compat hides /home/albinus/.emacs.d/elpa/debbugs-0.39/debbugs-compat
/home/albinus/.emacs.d/elpa/helm-3.9.7/helm-packages hides /home/albinus/.emacs.d/elpa/helm-core-3.9.7/helm-packages
~/lisp/telepathy hides /home/albinus/.emacs.d/elpa/telepathy-20131209.1258/telepathy
/home/albinus/src/elpa/packages/tramp-theme/tramp-theme-autoloads hides /home/albinus/.emacs.d/elpa/tramp-theme-0.2/tramp-theme-autoloads
/home/albinus/src/elpa/packages/tramp-theme/tramp-theme hides /home/albinus/.emacs.d/elpa/tramp-theme-0.2/tramp-theme
/home/albinus/src/elpa/packages/tramp-theme/tramp-theme-pkg hides /home/albinus/.emacs.d/elpa/tramp-theme-0.2/tramp-theme-pkg
/home/albinus/.emacs.d/elpa/hydra-0.15.0/lv hides /home/albinus/.emacs.d/elpa/lv-0.15.0/lv
/home/albinus/.emacs.d/elpa/transient-20240201.1100/transient hides /home/albinus/src/emacs/lisp/transient
/home/albinus/src/tramp/lisp/tramp-sudoedit hides /home/albinus/src/emacs/lisp/net/tramp-sudoedit
/home/albinus/src/tramp/lisp/tramp-message hides /home/albinus/src/emacs/lisp/net/tramp-message
/home/albinus/src/tramp/lisp/tramp-rclone hides /home/albinus/src/emacs/lisp/net/tramp-rclone
/home/albinus/src/tramp/lisp/tramp-gvfs hides /home/albinus/src/emacs/lisp/net/tramp-gvfs
/home/albinus/src/tramp/lisp/tramp-cmds hides /home/albinus/src/emacs/lisp/net/tramp-cmds
/home/albinus/src/tramp/lisp/tramp-sh hides /home/albinus/src/emacs/lisp/net/tramp-sh
/home/albinus/src/tramp/lisp/tramp-loaddefs hides /home/albinus/src/emacs/lisp/net/tramp-loaddefs
/home/albinus/src/tramp/lisp/tramp-cache hides /home/albinus/src/emacs/lisp/net/tramp-cache
/home/albinus/src/tramp/lisp/tramp-compat hides /home/albinus/src/emacs/lisp/net/tramp-compat
/home/albinus/src/tramp/lisp/tramp-ftp hides /home/albinus/src/emacs/lisp/net/tramp-ftp
/home/albinus/src/tramp/lisp/tramp-container hides /home/albinus/src/emacs/lisp/net/tramp-container
/home/albinus/src/tramp/lisp/tramp-archive hides /home/albinus/src/emacs/lisp/net/tramp-archive
/home/albinus/src/tramp/lisp/tramp-smb hides /home/albinus/src/emacs/lisp/net/tramp-smb
/home/albinus/src/tramp/lisp/tramp-uu hides /home/albinus/src/emacs/lisp/net/tramp-uu
/home/albinus/src/tramp/lisp/tramp-fuse hides /home/albinus/src/emacs/lisp/net/tramp-fuse
~/lisp/dbus hides /home/albinus/src/emacs/lisp/net/dbus
/home/albinus/src/tramp/lisp/trampver hides /home/albinus/src/emacs/lisp/net/trampver
/home/albinus/src/tramp/lisp/tramp-integration hides /home/albinus/src/emacs/lisp/net/tramp-integration
/home/albinus/src/tramp/lisp/tramp-sshfs hides /home/albinus/src/emacs/lisp/net/tramp-sshfs
/home/albinus/src/tramp/lisp/tramp hides /home/albinus/src/emacs/lisp/net/tramp
/home/albinus/src/tramp/lisp/tramp-crypt hides /home/albinus/src/emacs/lisp/net/tramp-crypt
/home/albinus/src/tramp/lisp/tramp-adb hides /home/albinus/src/emacs/lisp/net/tramp-adb

Features:
(shadow emacsbug nndoc gnus-dup misearch multi-isearch crm mule-util
display-line-numbers debbugs-gnu add-log debbugs-compat debbugs
soap-client warnings rng-xsd rng-dt rng-util xsd-regexp time-stamp
url-http url-gw url-auth gnus-gravatar gravatar dns url-cache sort
smiley gnus-cite mm-archive mail-extr textsec uni-scripts idna-mapping
ucs-normalize uni-confusable textsec-check gnus-async gnus-bcklg qp
gnus-ml debbugs-browse bug-reference disp-table pop3 utf-7 nndraft nnmh
nnml network-stream nsm gnus-agent gnus-srvr gnus-score score-mode
nnvirtual gnus-msg gnus-art mm-uu mml2015 mm-view mml-smime smime gnutls
dig gnus-cache gnus-sum shr pixel-fill kinsoku url-file svg dom nnnil
smtpmail gnus-demon nntp gnus-group gnus-undo gnus-start gnus-dbus dbus
xml gnus-cloud nnimap nnmail mail-source utf7 nnoo gnus-spec gnus-int
gnus-range message sendmail yank-media puny rfc822 mml mml-sec epa
derived epg rfc6068 epg-config mm-decode mm-bodies mm-encode mail-parse
rfc2231 rfc2047 rfc2045 ietf-drums mailabbrev gmm-utils mailheader
gnus-win gnus nnheader gnus-util mail-utils range mm-util mail-prsvr
cl-extra cl-print compile text-property-search comp-run comp-common
help-fns radix-tree face-remap ob-shell ob ob-tangle ol org-src ob-ref
ob-lob ob-table ob-exp ob-comint ob-core org-cycle org-fold
org-fold-core ob-eval org-keys oc org-compat org-version org-macs vc
vc-dispatcher time tramp-sh lxc-tramp lxd-tramp tramp trampver
tramp-integration files-x tramp-message help-mode tramp-compat xdg shell
pcomplete comint ansi-osc ring parse-time iso8601 time-date format-spec
ansi-color tramp-loaddefs rx delsel ido jka-compr icomplete cus-edit pp
cus-load icons wid-edit dired dired-loaddefs goto-addr thingatpt
alert-autoloads android-mode-autoloads auth-source-gopass-autoloads
auth-source-keytar-autoloads auth-source-kwallet-autoloads
auth-source-xoauth2-autoloads auto-sudoedit-autoloads
auto-virtualenv-autoloads auto-virtualenvwrapper-autoloads
boxquote-autoloads clang-format-autoloads company-shell-autoloads
company-autoloads counsel-toki-autoloads counsel-tramp-autoloads
counsel-autoloads dbus-codegen-autoloads debbugs-autoloads
dired-du-autoloads dired-rsync-autoloads dired-toggle-sudo-autoloads
direnv-autoloads disk-usage-autoloads dockerfile-mode-autoloads
editorconfig-charset-extras-autoloads
editorconfig-custom-majormode-autoloads
editorconfig-domain-specific-autoloads editorconfig-autoloads
editorconfig-generate-autoloads ednc-autoloads el-get-autoloads
envrc-autoloads etc-sudoers-mode-autoloads
exec-path-from-shell-autoloads fontaine-autoloads forge-autoloads
closql-autoloads emacsql-autoloads friendly-tramp-path-autoloads
fzf-autoloads ggtags-autoloads ghub-autoloads gited-autoloads
gitlab-ci-mode-flycheck-autoloads gitlab-ci-mode-autoloads
flycheck-autoloads gntp-autoloads gnus-desktop-notify-autoloads
helm-gitlab-autoloads helm-projectile-autoloads helm-autoloads
helm-core-autoloads async-autoloads ibuffer-tramp-autoloads
inheritenv-autoloads ivy-gitlab-autoloads gitlab-autoloads
journalctl-mode-autoloads keepass-mode-autoloads keytar-autoloads
kubernetes-autoloads log4e-autoloads lsp-java-autoloads
dap-mode-autoloads lsp-docker-autoloads bui-autoloads
lsp-latex-autoloads consult-autoloads lsp-treemacs-autoloads
lsp-mode-autoloads f-autoloads lxc-tramp-autoloads lxd-tramp-autoloads
magit-filenotify-autoloads magit-autoloads pcase git-commit-autoloads
magit-popup-autoloads magit-section-autoloads marcopolo-autoloads
mastodon-autoloads nexus-autoloads nnir-est-autoloads oauth2-autoloads
ob-restclient-autoloads orderless-autoloads persist-autoloads
pkg-info-autoloads epl-autoloads popup-autoloads projectile-autoloads
promise-autoloads pylint-autoloads python-environment-autoloads
deferred-autoloads pyvenv-autoloads
recentf-remove-sudo-tramp-prefix-autoloads request-autoloads
restclient-test-autoloads restclient-autoloads s3ed-autoloads finder-inf
slime-autoloads macrostep-autoloads spinner-autoloads
ssh-deploy-autoloads su-autoloads sudo-edit-autoloads sudo-ext-autoloads
sudo-utils-autoloads swiper-autoloads ivy-autoloads sx-autoloads
markdown-mode-autoloads telepathy-autoloads totp-autoloads
totp-auth-autoloads base32-autoloads tramp-theme-autoloads
transient-dwim-autoloads transient-autoloads treemacs-autoloads
cfrs-autoloads posframe-autoloads ht-autoloads pfuture-autoloads
ace-window-autoloads avy-autoloads treepy-autoloads uuid-autoloads
vdiff-autoloads hydra-autoloads lv-autoloads vertico-autoloads
virtualenv-autoloads virtualenvwrapper-autoloads s-autoloads
dash-autoloads web-server-autoloads wfnames-autoloads info
with-editor-autoloads yaml-autoloads yaml-mode-autoloads package
browse-url url url-proxy url-privacy url-expand url-methods url-history
url-cookie generate-lisp-file url-domsuf url-util mailcap url-handlers
url-parse auth-source cl-seq eieio eieio-core cl-macs password-cache
json subr-x map byte-opt gv bytecomp byte-compile url-vars cl-loaddefs
cl-lib rmc iso-transl tooltip cconv eldoc paren electric uniquify
ediff-hook vc-hooks lisp-float-type elisp-mode mwheel term/x-win x-win
term/common-win x-dnd touch-screen tool-bar dnd fontset image regexp-opt
fringe tabulated-list replace newcomment text-mode lisp-mode prog-mode
register page tab-bar menu-bar rfn-eshadow isearch easymenu timer select
scroll-bar mouse jit-lock font-lock syntax font-core term/tty-colors
frame minibuffer nadvice seq simple cl-generic indonesian philippine
cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao
korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech
european ethiopic indian cyrillic chinese composite emoji-zwj charscript
charprop case-table epa-hook jka-cmpr-hook help abbrev obarray oclosure
cl-preloaded button loaddefs theme-loaddefs faces cus-face macroexp
files window text-properties overlay sha1 md5 base64 format env
code-pages mule custom widget keymap hashtable-print-readable backquote
threads dbusbind inotify dynamic-setting system-font-setting
font-render-setting cairo gtk x-toolkit xinput2 x multi-tty move-toolbar
make-network-process native-compile emacs)

Memory information:
((conses 16 568029 79246) (symbols 48 25402 6)
 (strings 32 126758 19717) (string-bytes 1 3900820) (vectors 16 77655)
 (vector-slots 8 902415 39886) (floats 8 685 4342)
 (intervals 56 2954 2404) (buffers 984 30))

diff --git a/etc/images/README b/etc/images/README
index a778d9ce6c3..77377d36b5a 100644
--- a/etc/images/README
+++ b/etc/images/README
@@ -125,7 +125,7 @@ For more information see the adwaita-icon-theme repository at:

     https://gitlab.gnome.org/GNOME/adwaita-icon-theme

-Emacs images and their source in the Adwaita/scalable directory:
+Emacs images and their source in the Adwaita/symbolic directory:

   checked.svg               ui/checkbox-checked-symbolic.svg
   unchecked.svg             ui/checkbox-symbolic.svg
@@ -137,3 +137,5 @@ Emacs images and their source in the Adwaita/scalable directory:
   left.svg                  ui/pan-start-symbolic.svg
   right.svg                 ui/pan-end-symbolic.svg
   up.svg                    ui/pan-up-symbolic.svg
+  conceal.svg               actions/view-conceal-symbolic.svg
+  reveal.svg                actions/view-reveal-symbolic.svg
diff --git a/etc/images/conceal.svg b/etc/images/conceal.svg
new file mode 100644
index 00000000000..172b73ed3d3
--- /dev/null
+++ b/etc/images/conceal.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+    <path d="m 1.53125 0.46875 l -1.0625 1.0625 l 14 14 l 1.0625 -1.0625 l -2.382812 -2.382812 c 1.265624 -1.0625 2.171874 -2.496094 2.589843 -4.097657 c -0.914062 -3.523437 -4.097656 -5.984375 -7.738281 -5.988281 c -1.367188 0.011719 -2.707031 0.371094 -3.894531 1.042969 z m 6.46875 3.53125 c 2.210938 0 4 1.789062 4 4 c -0.003906 0.800781 -0.246094 1.578125 -0.699219 2.238281 l -1.46875 -1.46875 c 0.105469 -0.242187 0.164063 -0.503906 0.167969 -0.769531 c 0 -1.105469 -0.894531 -2 -2 -2 c -0.265625 0.003906 -0.527344 0.0625 -0.769531 0.167969 l -1.46875 -1.46875 c 0.660156 -0.453125 1.4375 -0.695313 2.238281 -0.699219 z m -6.144531 0.917969 c -0.753907 0.898437 -1.296875 1.957031 -1.59375 3.09375 c 0.914062 3.523437 4.097656 5.984375 7.738281 5.988281 c 0.855469 -0.007812 1.703125 -0.152344 2.511719 -0.425781 l -1.667969 -1.667969 c -0.277344 0.058594 -0.5625 0.089844 -0.84375 0.09375 c -2.210938 0 -4 -1.789062 -4 -4 c 0.003906 -0.28125 0.035156 -0.566406 0.09375 -0.84375 z m 0 0" fill="#2e3436"/>
+</svg>
diff --git a/etc/images/reveal.svg b/etc/images/reveal.svg
new file mode 100644
index 00000000000..41ae3733a53
--- /dev/null
+++ b/etc/images/reveal.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+    <path d="m 8 2 c -3.648438 0.003906 -6.832031 2.476562 -7.738281 6.007812 c 0.914062 3.527344 4.097656 5.988282 7.738281 5.992188 c 3.648438 -0.003906 6.832031 -2.476562 7.738281 -6.011719 c -0.914062 -3.523437 -4.097656 -5.984375 -7.738281 -5.988281 z m 0 2 c 2.210938 0 4 1.789062 4 4 s -1.789062 4 -4 4 s -4 -1.789062 -4 -4 s 1.789062 -4 4 -4 z m 0 2 c -1.105469 0 -2 0.894531 -2 2 s 0.894531 2 2 2 s 2 -0.894531 2 -2 s -0.894531 -2 -2 -2 z m 0 0" fill="#2e3436"/>
+</svg>
diff --git a/lisp/simple.el b/lisp/simple.el
index 9a33049f4ca..aa2e0507546 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -10858,6 +10858,80 @@ visible-mode
     (setq-local vis-mode-saved-buffer-invisibility-spec
                 buffer-invisibility-spec)
     (setq buffer-invisibility-spec nil)))
+
+
+;; It would be preferable to use "👁" ("\N{EYE}").  However, there is
+;; no corresponding Unicode char with a slash.  Therefore, we use images.
+(defvar read-passwd-show-password-image "reveal.svg"
+  "Mode line image to show a hidden password")
+
+(defvar read-passwd-hide-password-image "conceal.svg"
+  "Mode line image to hide a visible password")
+
+(defvar read-passwd-mode-line-buffer nil
+  "Buffer to modify `mode-line-format' for showing/hiding passwords.")
+
+(defvar read-passwd-mode-line-string nil
+  "Propertized mode line indicator for showing/hiding passwords.")
+
+(defvar read-passwd-mode-line-display nil
+  "Display properties for `read-passwd-mode-line-string'.")
+
+(defun read-passwd--toggle-visibility ()
+  "Toggle minibuffer contents visibility.
+Adapt also mode line."
+  (interactive)
+  (setq read-passwd--hide-password (not read-passwd--hide-password))
+  (when (display-graphic-p)
+    (with-current-buffer read-passwd-mode-line-buffer
+      (setq read-passwd-mode-line-display
+	    (find-image
+             `((:type svg
+                :file ,(if read-passwd--hide-password
+                           read-passwd-show-password-image
+                         read-passwd-hide-password-image))
+               :ascent center))
+            read-passwd-mode-line-string
+            `(:propertize " "
+              display ,read-passwd-mode-line-display
+              help-echo "mouse-1: Toggle password visibility"
+              mouse-face mode-line-highlight
+              local-map
+              (keymap
+               (mode-line keymap (mouse-1 . read-passwd--toggle-visibility)))))
+      (force-mode-line-update)))
+  (read-passwd--hide-password))
+
+(define-minor-mode read-passwd-mode
+  "Toggle visibility of password in minibuffer."
+  :group 'mode-line
+  :group 'minibuffer
+  :keymap read-passwd-map
+  :version "30.1"
+  (setq read-passwd--hide-password nil
+        ;; Stolen from `eldoc-minibuffer-message'.
+        read-passwd-mode-line-buffer
+        (window-buffer
+         (or (window-in-direction 'above (minibuffer-window))
+	     (minibuffer-selected-window)
+	     (get-largest-window))))
+
+  (when (display-graphic-p)
+    (if read-passwd-mode
+        (with-current-buffer read-passwd-mode-line-buffer
+          ;; Add `read-passwd-mode-line-string'.
+          (when (listp mode-line-format)
+            (setq mode-line-format
+                  (cons '(:eval read-passwd-mode-line-string)
+	                mode-line-format))))
+      (with-current-buffer read-passwd-mode-line-buffer
+        ;; Remove `read-passwd-mode-line-string'.
+        (when (listp mode-line-format)
+          (setq mode-line-format (cdr mode-line-format))))))
+
+  (when read-passwd-mode
+    (read-passwd--toggle-visibility)))
+
 
 (defvar messages-buffer-mode-map
   (let ((map (make-sparse-keymap)))
diff --git a/lisp/subr.el b/lisp/subr.el
index c317d558e24..555d0084545 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -3375,14 +3375,23 @@ read-passwd-map
   (let ((map (make-sparse-keymap)))
     (set-keymap-parent map minibuffer-local-map)
     (define-key map "\C-u" #'delete-minibuffer-contents) ;bug#12570
+    (define-key map "\t" #'read-passwd--toggle-visibility)
     map)
   "Keymap used while reading passwords.")

-(defun read-password--hide-password ()
+(defvar read-passwd--hide-password t)
+
+(defun read-passwd--hide-password ()
+  "Make password in minibuffer hidden or visible."
   (let ((beg (minibuffer-prompt-end)))
     (dotimes (i (1+ (- (buffer-size) beg)))
-      (put-text-property (+ i beg) (+ 1 i beg)
-                         'display (string (or read-hide-char ?*))))))
+      (if read-passwd--hide-password
+          (put-text-property
+           (+ i beg) (+ 1 i beg) 'display (string (or read-hide-char ?*)))
+        (remove-list-of-text-properties (+ i beg) (+ 1 i beg) '(display)))
+      (put-text-property
+       (+ i beg) (+ 1 i beg)
+       'help-echo "C-u: Clear password\nTAB: Toggle password visibility"))))

 (defun read-passwd (prompt &optional confirm default)
   "Read a password, prompting with PROMPT, and return it.
@@ -3420,18 +3429,20 @@ read-passwd
             (setq-local inhibit-modification-hooks nil) ;bug#15501.
 	    (setq-local show-paren-mode nil)		;bug#16091.
             (setq-local inhibit--record-char t)
-            (add-hook 'post-command-hook #'read-password--hide-password nil t))
+            (read-passwd-mode 1)
+            (add-hook 'post-command-hook #'read-passwd--hide-password nil t))
         (unwind-protect
             (let ((enable-recursive-minibuffers t)
 		  (read-hide-char (or read-hide-char ?*)))
               (read-string prompt nil t default)) ; t = "no history"
           (when (buffer-live-p minibuf)
             (with-current-buffer minibuf
+              (read-passwd-mode -1)
               ;; Not sure why but it seems that there might be cases where the
               ;; minibuffer is not always properly reset later on, so undo
               ;; whatever we've done here (bug#11392).
               (remove-hook 'after-change-functions
-                           #'read-password--hide-password 'local)
+                           #'read-passwd--hide-password 'local)
               (kill-local-variable 'post-self-insert-hook)
               ;; And of course, don't keep the sensitive data around.
               (erase-buffer))))))))