[PATCH] Add support for XKCD-style wordlist passwords

Adhityaa Chandrasekar pass at adtac.in
Tue Oct 30 06:00:36 CET 2018


---
 man/pass.1            | 17 ++++++++++-------
 src/password-store.sh | 16 +++++++++++-----
 2 files changed, 21 insertions(+), 12 deletions(-)

This is my first time contributing to pass as a long-time user. In this
patch, I'm adding support for XKCD-style passwords [1] composed of words
from a wordlist. Since there is no standardised location for a
dictionary of words, the user has to specify a path to a file with the
--wordlist argument.

I don't know much bash/zsh/fish completions, so I'm afraid someone else
will have to help with that.

[1] https://xkcd.com/936/

 - Adhityaa

diff --git a/man/pass.1 b/man/pass.1
index 01a3fbe..72a83fc 100644
--- a/man/pass.1
+++ b/man/pass.1
@@ -122,15 +122,18 @@ ensure that temporary files are created in \fI/dev/shm\fP in order to avoid writ
 difficult-to-erase disk sectors. If \fI/dev/shm\fP is not accessible, fallback to
 the ordinary \fITMPDIR\fP location, and print a warning.
 .TP
-\fBgenerate\fP [ \fI--no-symbols\fP, \fI-n\fP ] [ \fI--clip\fP, \fI-c\fP ] [ \fI--in-place\fP, \fI-i\fP | \fI--force\fP, \fI-f\fP ] \fIpass-name [pass-length]\fP
+\fBgenerate\fP [ \fI--no-symbols\fP, \fI-n\fP ] [ \fI--wordlist /path/to/wordlist\fP, \fI-w /path/to/wordlist\fP ][ \fI--clip\fP, \fI-c\fP ] [ \fI--in-place\fP, \fI-i\fP | \fI--force\fP, \fI-f\fP ] \fIpass-name [pass-length]\fP
 Generate a new password using \fB/dev/urandom\fP of length \fIpass-length\fP
 (or \fIPASSWORD_STORE_GENERATED_LENGTH\fP if unspecified) and insert into
-\fIpass-name\fP. If \fI--no-symbols\fP or \fI-n\fP is specified, do not use
-any non-alphanumeric characters in the generated password. The character sets used
-in generating passwords can be changed with the \fIPASSWORD_STORE_CHARACTER_SET\fP and
-\fIPASSWORD_STORE_CHARACTER_SET_NO_SYMBOLS\fP environment variables, described below.
-If \fI--clip\fP or \fI-c\fP is specified, do not print the password but instead copy
-it to the clipboard using
+\fIpass-name\fP. If \fI--no-symbols\fP or \fI-n\fP is specified, do not use any
+non-alphanumeric characters in the generated password. If \fI--wordlist\fP is
+specified, along with a file as argument containing a list of words to choose
+from, four words will be randomly chosen and used as password. The character
+sets used in generating passwords can be changed with the
+\fIPASSWORD_STORE_CHARACTER_SET\fP and
+\fIPASSWORD_STORE_CHARACTER_SET_NO_SYMBOLS\fP environment variables, described
+below.  If \fI--clip\fP or \fI-c\fP is specified, do not print the password but
+instead copy it to the clipboard using
 .BR xclip (1)
 and then restore the clipboard after 45 (or \fIPASSWORD_STORE_CLIP_TIME\fP) seconds. If \fI--qrcode\fP
 or \fI-q\fP is specified, do not print the password but instead display a QR code using
diff --git a/src/password-store.sh b/src/password-store.sh
index d89d455..2778c26 100755
--- a/src/password-store.sh
+++ b/src/password-store.sh
@@ -491,12 +491,13 @@ cmd_edit() {
 }
 
 cmd_generate() {
-	local opts qrcode=0 clip=0 force=0 characters="$CHARACTER_SET" inplace=0 pass
-	opts="$($GETOPT -o nqcif -l no-symbols,qrcode,clip,in-place,force -n "$PROGRAM" -- "$@")"
+	local opts qrcode=0 clip=0 force=0 characters="$CHARACTER_SET" wordlist=0 inplace=0 pass
+	opts="$($GETOPT -o nw:qcif -l no-symbols,wordlist:,qrcode,clip,in-place,force -n "$PROGRAM" -- "$@")"
 	local err=$?
 	eval set -- "$opts"
 	while true; do case $1 in
 		-n|--no-symbols) characters="$CHARACTER_SET_NO_SYMBOLS"; shift ;;
+		-w|--wordlist) wordlist=$2; shift; shift ;;
 		-q|--qrcode) qrcode=1; shift ;;
 		-c|--clip) clip=1; shift ;;
 		-f|--force) force=1; shift ;;
@@ -504,7 +505,7 @@ cmd_generate() {
 		--) shift; break ;;
 	esac done
 
-	[[ $err -ne 0 || ( $# -ne 2 && $# -ne 1 ) || ( $force -eq 1 && $inplace -eq 1 ) || ( $qrcode -eq 1 && $clip -eq 1 ) ]] && die "Usage: $PROGRAM $COMMAND [--no-symbols,-n] [--clip,-c] [--qrcode,-q] [--in-place,-i | --force,-f] pass-name [pass-length]"
+	[[ $err -ne 0 || ( $# -ne 2 && $# -ne 1 ) || ( $force -eq 1 && $inplace -eq 1 ) || ( $qrcode -eq 1 && $clip -eq 1 ) ]] && die "Usage: $PROGRAM $COMMAND [--no-symbols,-n] [--wordlist,-w /path/to/wordlist] [--clip,-c] [--qrcode,-q] [--in-place,-i | --force,-f] pass-name [pass-length]"
 	local path="$1"
 	local length="${2:-$GENERATED_LENGTH}"
 	check_sneaky_paths "$path"
@@ -517,8 +518,13 @@ cmd_generate() {
 
 	[[ $inplace -eq 0 && $force -eq 0 && -e $passfile ]] && yesno "An entry already exists for $path. Overwrite it?"
 
-	read -r -n $length pass < <(LC_ALL=C tr -dc "$characters" < /dev/urandom)
-	[[ ${#pass} -eq $length ]] || die "Could not generate password from /dev/urandom."
+	if [[ "$wordlist" != "" ]]; then
+		pass=$(shuf -n 4 "$wordlist" | paste -sd "-" -)
+	else
+		read -r -n $length pass < <(LC_ALL=C tr -dc "$characters" < /dev/urandom)
+		[[ ${#pass} -eq $length ]] || die "Could not generate password from /dev/urandom."
+	fi
+
 	if [[ $inplace -eq 0 ]]; then
 		echo "$pass" | $GPG -e "${GPG_RECIPIENT_ARGS[@]}" -o "$passfile" "${GPG_OPTS[@]}" || die "Password encryption aborted."
 	else
-- 
2.19.1



More information about the Password-Store mailing list