Password Manager

Jul 21 2022 · LastMod: Jul 21 2022

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

 1-----BEGIN PGP MESSAGE-----
 2
 3hQIMA21DdFXR20c9AQ/9FKArzUEDuN4YWBeINf+xiFePyx5V/rsAlSq+sQVs70Qb
 4zxXu8fyL5iD/2YO7VLZdho/VOv1KeowtoANv5Ntb587SsUVxX5uhL/XQTy3BGj8f
 50AY8vMG4yzn5Njr/INkClxS5uPYcXN2/bNKz/fydNqJ4GTpb9a5L1SRYQLbqtFow
 6rACv4C2YYttrW0XRRR48kohpFw9MaQ/i8Dq7Xo+LvIOWLodHf/AtGKJ/33TCplm5
 7spG2p3Z/dnYcTcYL3XhiF/jMsPZY/P9UWDHvkUK6EFCUx2EVGe1d6G+rsQF+CH7a
 8kE8QJsbZbQATvIfBDNxFtn3PKAK3VKChIEOWWWDorHa2JaqDjYMe1q1cQa7tb/Wj
 9hkmI77zb9Uy7UNJKs4J8AGF8ButGbPptB0zxAStneJnKeiA2xb1ud8zwe+rG07pE
10XJcAKm23J1COU6rlk5p0i3iKPmBMnZ/zmidMXXB9iWFVSLrgnaDI0z+Jwqo//jLv
11ybkUlHxCMIjaW9YVtfaal2iEK09O84FeC59hY3/yHsV8whcQX3/fVjdeykjMZacT
12lKG79OTImAvzLYWOxcFjC1vBxK5qaOhRoTrzKvycoKWQHrmcDfPQV+tPA+9sNYPU
13VYEfIS8K7vNODvO7giL5jywbg0JPMh9EuVoQMi6G9nmZAV8lyVeG0QoNiy3SA5jS
14wQgBIvIOBfuPxhkdrH8ihGlZqWKDXawB6cUaYbp2ROSro2ucYmUW8MUdMCCaa6lf
15ucEBSygZWCAyGITxTsk9XVHXqKkiFzZOn3S/4hWmerNSdZO95eDz7l+QSGkTwQoZ
16Ykk6U6JWt7JQOb1fW4qjm3BLpkS170QD6aIOrsJZYxtNSEPDw4624gpat8lMRSTD
17oJkB9nPmQ+8ksc3ds0fFdAd4V6Yq5GQ3uG7XzwZgb3NyRLJ8vzHQ4vdGrEi3AT+n
18argR0S2N+qkMGGlNe8RX4VVslyf/dkzbRKg+jokw8Z3gULXE+mjeZGxTRYVNdkId
19CrRFa+bQ5ptjsfJkREoPIj/8XiFOJbnbtpmEIdi8GfChrYPhsa+PS7y22HNXsuPH
20/aRylJvAnP9rHc+1URSMFwYvMwtMwQClMfEFoBwc5jZIUUG+uUzFkL/cJqxtgQ0E
21vPLL9+NECg8oAOd29k8uEOuOGlly1038sWohohak7+0MXWMO6GZwonWspFONGeMF
22sUKhB8mVh5vcuhyROlFYi97MDJI7abbpm/cGhcHm2fsHwSK5rAQK1keb033rx+tj
23nuvFC45MKQEvf4//DrCgKd1oBmSqtGbybQg=
24=dune
25-----END PGP MESSAGE-----

Note that

  1#!/usr/bin/env bash
  2
  3set -Eeuo pipefail
  4
  5BIN_NAME=${0##*/}
  6MENU=${PASSWORD_MENU:-fzf}
  7PASSWORD_STORE=${PASSWORD_STORE:-$HOME/.password}
  8GREPPER=${PASSWORD_GREPPER:-grep}
  9EDITOR=${EDITOR:-vim}
 10PASS_LENGTH=${PASSWORD_LENGTH:-14}
 11PASS_COPY=${PASS_COPY:=xclip -selection clipboard}
 12PASS_TIMEOUT_SEC=${PASS_TIMEOUT:=15}
 13
 14
 15umask 077
 16TMP=`mktemp`
 17trap "[ -n "$TMP" ] && rm -f $TMP" EXIT SIGHUP SIGINT SIGQUIT SIGKILL
 18
 19die(){
 20	echo "$@" >&2
 21	exit 1
 22}
 23
 24pass_copy(){
 25	echo "Clearing clipboard in $PASS_TIMEOUT_SEC seconds."
 26	{
 27		sleep "$PASS_TIMEOUT_SEC" || kill 0
 28		setsid $PASS_COPY </dev/null
 29	} &
 30	$PASS_COPY <<< "$1"
 31}
 32
 33#
 34#pass_copy(){
 35#	$PASS_COPY <<< "$1"
 36#	echo "Clearing clipboard in $PASS_TIMEOUT_SEC seconds."
 37#        setsid clearclip $PASS_TIMEOUT_SEC &
 38#}
 39#
 40# with
 41#
 42# clearclip
 43#
 44# #!/usr/bin/env sh
 45#
 46# sleep $1
 47# xsel -c -b
 48
 49
 50get_pass(){
 51	gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
 52	local chosen=$(awk '{print $1 "\t" $2 "\t\t" $5}' < $TMP | $MENU | awk '{print $1}' )
 53	[ -z "$chosen" ] && die "Nothing chosen"
 54	local pass=$($GREPPER ^"$chosen" < $TMP | awk '{print $4}' )
 55	pass_copy "$pass"
 56}
 57
 58get_username(){
 59	gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
 60	local chosen=$(awk '{print $1 "\t" $2 "\t\t" $5}' < $TMP | $MENU | awk '{print $1}' )
 61	[ -z "$chosen" ] && die "Nothing chosen"
 62	local name=$($GREPPER ^"$chosen" < $TMP | awk '{print $3}' )
 63	pass_copy "$name"
 64}
 65
 66get_all(){
 67	gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
 68	local chosen=$(awk '{print $1 "\t" $2 "\t\t" $5}' < $TMP | $MENU | awk '{print $1}' )
 69	[ -z "$chosen" ] && die "Nothing chosen"
 70	local all=$($GREPPER ^"$chosen" < $TMP | awk '{print $3"\n"$4}' )
 71	echo $all | awk '{print $1}'
 72	local pass="$(echo $all | awk '{print $2}')"
 73	pass_copy "$pass"
 74}
 75
 76edit_pass(){
 77	gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
 78	$EDITOR $TMP
 79	gpg -ae < $TMP > $PASSWORD_STORE
 80}
 81
 82show(){
 83	gpg -d $PASSWORD_STORE 2>/dev/null
 84}
 85
 86gen_pass(){
 87	LC_ALL=C </dev/urandom tr -dc '1234567890!@#$%^&*(){}[]qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' | head -c$PASS_LENGTH ; echo ""
 88}
 89
 90
 91new_pass(){
 92	gpg -d $PASSWORD_STORE 2>/dev/null > $TMP
 93	local uid
 94	local name
 95	local tag
 96	local pass
 97	local addi
 98	read -p "Tag (without space):" tag
 99	[ -z "$tag" ] && die "No tag specified"
100	[ -n "$(echo $tag | awk '{print $2}')" ] && die "No space allowed"
101	echo
102	read -p "Username (without space) :" name
103	[ -z "$name" ] && die "No username specified"
104	[ -n "$(echo $name | awk '{print $2}')" ] && die "No space allowed"
105	echo
106	read -p "Additional info (only the first word will be printed when querying):" addi
107	echo
108	pass=$(gen_pass)
109	uid=$(echo "$tag $name $pass $addi" | md5sum | head -c5)
110	echo "$uid $tag $name $pass $addi" >> $TMP
111	gpg -ae < $TMP > $PASSWORD_STORE
112	echo "New account information stored as"
113	echo "$uid $tag $name PASSWORD $addi"
114	pass_copy "$pass" && echo "Password copied to the clipboard"
115}
116
117help(){
118cat <<EOF
119password file format:
120	UID TAG USERNAME PASSWORD ADDITIONAL_INFO
121	- Ensure that UIDs do not coincide when editing the password file by hand.
122        - Except for the last one, all the fields should be free from blank spaces.
123        - Only the first column of the ADDITIONAL_INFO field will be printed when querying.
124        - To check all the information stored use $BIN_NAME show | grep ^<UID>.
125
126	$BIN_NAME h[elp] 			Show this help message
127	$BIN_NAME e[dit] 			Edit the password file
128	$BIN_NAME a[ll]  			Get username and password (username will be printed and password will be copied)
129	$BIN_NAME i[d] 		                Get username
130	$BIN_NAME n[ew] 			Add a new account
131	$BIN_NAME s[how] 			Decrypt and show the password-store file
132	$BIN_NAME 		        	Get password
133EOF
134}
135
136case ${1-default} in
137    e*) edit_pass ;;
138    i*) get_username ;;
139    a*) get_all ;;
140    n* ) new_pass ;;
141    s* ) show ;;
142    "default" ) get_pass ;;
143    * ) help ;;
144esac