Password Manager

A password manager written in bash (not POSIX shell but bash). Requires fzf or demnu and such for fuzzy-finding the STDIN inputs and write the selected items tothe STDOUT. password encrypts and stores passwords via pgp in a single file (PASSWORD_STORE), upon query the file is decrypted to a temp file. The temp file will be deleted afterwards. This is a crude clones of pash, written since I don’t want my id’s to be exposed as the names of the .gpg files. You will be able to upload the PASSWORD_STORE file to you e.g. github repository without exposing any of your account information. For instance, mine

-----BEGIN PGP MESSAGE-----

hQIMA21DdFXR20c9AQ/9FKArzUEDuN4YWBeINf+xiFePyx5V/rsAlSq+sQVs70Qb
zxXu8fyL5iD/2YO7VLZdho/VOv1KeowtoANv5Ntb587SsUVxX5uhL/XQTy3BGj8f
0AY8vMG4yzn5Njr/INkClxS5uPYcXN2/bNKz/fydNqJ4GTpb9a5L1SRYQLbqtFow
rACv4C2YYttrW0XRRR48kohpFw9MaQ/i8Dq7Xo+LvIOWLodHf/AtGKJ/33TCplm5
spG2p3Z/dnYcTcYL3XhiF/jMsPZY/P9UWDHvkUK6EFCUx2EVGe1d6G+rsQF+CH7a
kE8QJsbZbQATvIfBDNxFtn3PKAK3VKChIEOWWWDorHa2JaqDjYMe1q1cQa7tb/Wj
hkmI77zb9Uy7UNJKs4J8AGF8ButGbPptB0zxAStneJnKeiA2xb1ud8zwe+rG07pE
XJcAKm23J1COU6rlk5p0i3iKPmBMnZ/zmidMXXB9iWFVSLrgnaDI0z+Jwqo//jLv
ybkUlHxCMIjaW9YVtfaal2iEK09O84FeC59hY3/yHsV8whcQX3/fVjdeykjMZacT
lKG79OTImAvzLYWOxcFjC1vBxK5qaOhRoTrzKvycoKWQHrmcDfPQV+tPA+9sNYPU
VYEfIS8K7vNODvO7giL5jywbg0JPMh9EuVoQMi6G9nmZAV8lyVeG0QoNiy3SA5jS
wQgBIvIOBfuPxhkdrH8ihGlZqWKDXawB6cUaYbp2ROSro2ucYmUW8MUdMCCaa6lf
ucEBSygZWCAyGITxTsk9XVHXqKkiFzZOn3S/4hWmerNSdZO95eDz7l+QSGkTwQoZ
Ykk6U6JWt7JQOb1fW4qjm3BLpkS170QD6aIOrsJZYxtNSEPDw4624gpat8lMRSTD
oJkB9nPmQ+8ksc3ds0fFdAd4V6Yq5GQ3uG7XzwZgb3NyRLJ8vzHQ4vdGrEi3AT+n
argR0S2N+qkMGGlNe8RX4VVslyf/dkzbRKg+jokw8Z3gULXE+mjeZGxTRYVNdkId
CrRFa+bQ5ptjsfJkREoPIj/8XiFOJbnbtpmEIdi8GfChrYPhsa+PS7y22HNXsuPH
/aRylJvAnP9rHc+1URSMFwYvMwtMwQClMfEFoBwc5jZIUUG+uUzFkL/cJqxtgQ0E
vPLL9+NECg8oAOd29k8uEOuOGlly1038sWohohak7+0MXWMO6GZwonWspFONGeMF
sUKhB8mVh5vcuhyROlFYi97MDJI7abbpm/cGhcHm2fsHwSK5rAQK1keb033rx+tj
nuvFC45MKQEvf4//DrCgKd1oBmSqtGbybQg=
=dune
-----END PGP MESSAGE-----

Note that

#!/usr/bin/env bash

set -Eeuo pipefail

BIN_NAME=${0##*/}
MENU=${PASSWORD_MENU:-fzf}
PASSWORD_STORE=${PASSWORD_STORE:-$HOME/.password}
GREPPER=${PASSWORD_GREPPER:-grep}
EDITOR=${EDITOR:-vim}
PASS_LENGTH=${PASSWORD_LENGTH:-14}
PASS_COPY=${PASS_COPY:=xclip -selection clipboard}
PASS_TIMEOUT_SEC=${PASS_TIMEOUT:=15}


umask 077
TMP=`mktemp`
trap "[ -n "$TMP" ] && rm -f $TMP" EXIT SIGHUP SIGINT SIGQUIT SIGKILL

die(){
	echo "$@" >&2
	exit 1
}

pass_copy(){
	echo "Clearing clipboard in $PASS_TIMEOUT_SEC seconds."
	{
		sleep "$PASS_TIMEOUT_SEC" || kill 0
		setsid $PASS_COPY </dev/null
	} &
	$PASS_COPY <<< "$1"
}

#
#pass_copy(){
#	$PASS_COPY <<< "$1"
#	echo "Clearing clipboard in $PASS_TIMEOUT_SEC seconds."
#        setsid clearclip $PASS_TIMEOUT_SEC &
#}
#
# with
#
# clearclip
#
# #!/usr/bin/env sh
#
# sleep $1
# xsel -c -b


get_pass(){
	gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
	local chosen=$(awk '{print $1 "\t" $2 "\t\t" $5}' < $TMP | $MENU | awk '{print $1}' )
	[ -z "$chosen" ] && die "Nothing chosen"
	local pass=$($GREPPER ^"$chosen" < $TMP | awk '{print $4}' )
	pass_copy "$pass"
}

get_username(){
	gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
	local chosen=$(awk '{print $1 "\t" $2 "\t\t" $5}' < $TMP | $MENU | awk '{print $1}' )
	[ -z "$chosen" ] && die "Nothing chosen"
	local name=$($GREPPER ^"$chosen" < $TMP | awk '{print $3}' )
	pass_copy "$name"
}

get_all(){
	gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
	local chosen=$(awk '{print $1 "\t" $2 "\t\t" $5}' < $TMP | $MENU | awk '{print $1}' )
	[ -z "$chosen" ] && die "Nothing chosen"
	local all=$($GREPPER ^"$chosen" < $TMP | awk '{print $3"\n"$4}' )
	echo $all | awk '{print $1}'
	local pass="$(echo $all | awk '{print $2}')"
	pass_copy "$pass"
}

edit_pass(){
	gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
	$EDITOR $TMP
	gpg -ae < $TMP > $PASSWORD_STORE
}

show(){
	gpg -d $PASSWORD_STORE 2>/dev/null
}

gen_pass(){
	LC_ALL=C </dev/urandom tr -dc '1234567890!@#$%^&*(){}[]qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' | head -c$PASS_LENGTH ; echo ""
}


new_pass(){
	gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
	local uid
	local name
	local tag
	local pass
	local addi
	read -p "Tag (without space):" tag
	[ -z "$tag" ] && die "No tag specified"
	[ -n "$(echo $tag | awk '{print $2}')" ] && die "No space allowed"
	echo
	read -p "Username (without space) :" name
	[ -z "$name" ] && die "No username specified"
	[ -n "$(echo $name | awk '{print $2}')" ] && die "No space allowed"
	echo
	read -p "Additional info (only the first word will be printed when querying):" addi
	echo
	pass=$(gen_pass)
	uid=$(echo "$tag $name $pass $addi" | md5sum | head -c5)
	echo "$uid $tag $name $pass $addi" >> $TMP
	gpg -ae < $TMP > $PASSWORD_STORE
	echo "New account information stored as"
	echo "$uid $tag $name PASSWORD $addi"
	pass_copy "$pass" && echo "Password copied to the clipboard"
}

help(){
cat <<EOF
password file format:
	UID TAG USERNAME PASSWORD ADDITIONAL_INFO
	- Ensure that UIDs do not coincide when editing the password file by hand.
        - Except for the last one, all the fields should be free from blank spaces.
        - Only the first column of the ADDITIONAL_INFO field will be printed when querying.
        - To check all the information stored use $BIN_NAME show | grep ^<UID>.

	$BIN_NAME h[elp] 			Show this help message
	$BIN_NAME e[dit] 			Edit the password file
	$BIN_NAME a[ll]  			Get username and password (username will be printed and password will be copied)
	$BIN_NAME i[d] 		                Get username
	$BIN_NAME n[ew] 			Add a new account
	$BIN_NAME s[how] 			Decrypt and show the password-store file
	$BIN_NAME 		        	Get password
EOF
}

case ${1-default} in
    e*) edit_pass ;;
    i*) get_username ;;
    a*) get_all ;;
    n* ) new_pass ;;
    s* ) show ;;
    "default" ) get_pass ;;
    * ) help ;;
esac