Purse/pwd.sh

151 lines
2.9 KiB
Bash
Executable File

#!/bin/bash
#
# Script for managing passwords in a symmetrically encrypted file using GnuPG.
set -o errtrace
set -o nounset
set -o pipefail
gpg=$(which gpg)
safe=pwd.sh.safe
fail () {
# Print an error message and exit.
tput setaf 1 ; echo "Error: ${1}"
exit 1
}
get_pass () {
# Prompt for a password.
unset password
prompt="${1}"
while IFS= read -p "${prompt}" -r -s -n 1 char ; do
if [[ ${char} == $'\0' ]] ; then
break
fi
prompt='*'
password+="${char}"
done
if [ -z ${password+x} ] ; then
fail "No password provided"
fi
}
decrypt () {
# Decrypt with a password.
${gpg} \
--decrypt --armor --batch \
--command-fd 0 --passphrase "${1}" "${2}" 2>/dev/null
}
encrypt () {
# Encrypt with a password.
${gpg} \
--symmetric --armor --batch --yes \
--command-fd 0 --passphrase "${1}" \
--output "${2}" "${3}" 2>/dev/null
}
read_pass () {
# Read a password from safe.
if [ ! -s ${safe} ] ; then
fail "No passwords found"
else
get_pass "Enter password to unlock ${safe}: " ; echo
decrypt ${password} ${safe} || fail "Decryption failed"
fi
}
gen_pass () {
# Generate a password.
len=40
read -p "Password length? (default: 40, max: 80) " length
if [[ ${length} =~ ^[0-9]+$ ]] ; then
len=${length}
fi
${gpg} --gen-random -a 0 80 | cut -c -${len}
}
write_pass () {
# Write a password in safe.
# If no password provided, clear the entry by writing an empty line.
if [ -z ${userpass+x} ] ; then
new_entry=" "
else
new_entry="${userpass} ${username}"
fi
get_pass "Enter password to unlock ${safe}: " ; echo
# If safe exists, decrypt it and filter out username, or bail on error.
# If successful, append new entry, or blank line.
# Filter out any blank lines.
# Finally, encrypt it all to a new safe file, or fail.
# If successful, update to new safe file.
( if [ -f ${safe} ] ; then
decrypt ${password} ${safe} | \
grep -v -e " ${username}$" || return
fi ; \
echo "${new_entry}") | \
grep -v -e "^[[:space:]]*$" | \
encrypt ${password} ${safe}.new - || fail "Write to safe failed"
mv ${safe}.new ${safe}
}
create_username () {
# Create a new username and password.
read -p "Username: " username
read -p "Generate password? (y/n, default: y) " rand_pass
if [ "${rand_pass}" == "n" ]; then
get_pass "Enter password for \"${username}\": " ; echo
userpass=$password
else
userpass=$(gen_pass)
fi
}
sanity_check () {
# Make sure required programs are installed and can be executed.
if [[ -z ${gpg} && ! -x ${gpg} ]] ; then
fail "GnuPG is not available"
fi
}
sanity_check
read -p "Read, write, or delete a password? (r/w/d, default: r) " action
if [ "${action}" == "w" ] ; then
create_username && write_pass
elif [ "${action}" == "d" ] ; then
read -p "Username to delete? " username
write_pass
else
read_pass
fi
tput setaf 2 ; echo "Done"