mirror of https://github.com/octoleo/Purse.git
Compare commits
22 Commits
Author | SHA1 | Date |
---|---|---|
drduh | 8f09cf87e3 | |
drduh | abcb88a032 | |
drduh | c8ede9797a | |
drduh | 1b990c96a6 | |
drduh | e6c3828504 | |
drduh | 404d5402d1 | |
drduh | f429a52625 | |
drduh | 02b910b326 | |
drduh | 80d501cba8 | |
drduh | 2c02ec96a3 | |
drduh | 15bb2a9dc8 | |
drduh | 0ca2711773 | |
drduh | 84a98927c4 | |
drduh | 1370d1ee13 | |
drduh | 5cc6c81ed0 | |
drduh | 7e53a6b510 | |
drduh | 9df8f4c58c | |
drduh | 6976cff500 | |
Dreary Lisper | acca8834a4 | |
drduh | 7074a8bf4c | |
drduh | 883e68a95f | |
drduh | 8d9ca6c14d |
|
@ -0,0 +1 @@
|
|||
github: [drduh]
|
|
@ -0,0 +1,3 @@
|
|||
purse.*.tar
|
||||
purse.index*
|
||||
safe/
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2019 drduh
|
||||
Copyright (c) 2018 drduh
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
90
README.md
90
README.md
|
@ -1,72 +1,88 @@
|
|||
# Purse
|
||||
|
||||
![screencast gif](https://user-images.githubusercontent.com/12475110/40880505-3834ce1c-6667-11e8-89d0-6961886842c6.gif)
|
||||
|
||||
Purse is a fork of [drduh/pwd.sh](https://github.com/drduh/pwd.sh).
|
||||
|
||||
Both programs are shell scripts which use [GPG](https://www.gnupg.org/) to manage passwords in an encrypted text file. Purse uses asymmetric (public-key) authentication, while pwd.sh uses symmetric (password-based) authentication.
|
||||
Both programs are Bash shell scripts which use [GnuPG](https://www.gnupg.org/) to manage passwords and other secrets in encrypted text files. Purse is based on asymmetric (public-key) authentication, while pwd.sh is based on symmetric (password-based) authentication.
|
||||
|
||||
While both scripts use a trusted crypto implementation (GPG) and safely handle passwords (never saving plaintext to disk), Purse eliminates the need to remember and use a master password - just plug in a YubiKey, enter the PIN, then touch it to decrypt the password safe to stdout.
|
||||
While both scripts use a trusted crypto implementation (GnuPG) and safely handle passwords (never saving plaintext to disk, only using shell built-ins), Purse eliminates the need to remember a main passphrase - just plug in a YubiKey, enter the PIN, then touch it to decrypt a password to clipboard.
|
||||
|
||||
By using Purse with YubiKey, the risk of master password phishing and keylogging is eliminated - only physical possession of the key AND knowledge of the PIN can unlock the password safe.
|
||||
# Install
|
||||
|
||||
# Installation
|
||||
This script requires a GnuPG identity - see [drduh/YubiKey-Guide](https://github.com/drduh/YubiKey-Guide) to set one up.
|
||||
|
||||
This script requires a GPG identity - see [drduh/YubiKey-Guide](https://github.com/drduh/YubiKey-Guide) to set one up.
|
||||
|
||||
To install Purse:
|
||||
For the latest version, clone the repository or download the script directly:
|
||||
|
||||
```console
|
||||
$ git clone https://github.com/drduh/Purse
|
||||
git clone https://github.com/drduh/Purse
|
||||
|
||||
wget https://github.com/drduh/Purse/blob/master/purse.sh
|
||||
```
|
||||
|
||||
Edit `purse.sh` to specify your GPG key ID.
|
||||
Versioned [Releases](https://github.com/drduh/Purse/releases) are also available.
|
||||
|
||||
# Use
|
||||
|
||||
`cd Purse` and run the script interactively using `./purse.sh`
|
||||
Run the script interactively using `./purse.sh` or symlink to a directory in `PATH`:
|
||||
|
||||
* Type `w` to write a password.
|
||||
* Type `r` to read a password.
|
||||
* Type `d` to delete a password.
|
||||
* Type `h` to print the help text.
|
||||
- `w` to write a password
|
||||
- `r` to read a password
|
||||
- `l` to list passwords
|
||||
- `b` to create an archive for backup
|
||||
- `h` to print the help text
|
||||
|
||||
Examples:
|
||||
Options can also be passed on the command line.
|
||||
|
||||
Create 30-character password for `gmail`:
|
||||
Create a 20-character password for `userName`:
|
||||
|
||||
```console
|
||||
$ ./purse.sh w gmail 30
|
||||
./purse.sh w userName 20
|
||||
```
|
||||
|
||||
Append `q` to create a password without displaying it.
|
||||
|
||||
Read password for `user@github`:
|
||||
Read password for `userName`:
|
||||
|
||||
```console
|
||||
$ ./purse.sh r user@github
|
||||
./purse.sh r userName
|
||||
```
|
||||
|
||||
Delete password for `reddit`:
|
||||
Passwords are stored with an epoch timestamp for revision control. The most recent version is copied to clipboard on read. To list all passwords or read a specific version of a password:
|
||||
|
||||
```console
|
||||
$ ./purse.sh d reddit
|
||||
./purse.sh l
|
||||
|
||||
./purse.sh r userName@1574723600
|
||||
```
|
||||
|
||||
Copy password for `github` to clipboard (substitute `pbcopy` on macOS):
|
||||
Create an archive for backup:
|
||||
|
||||
```console
|
||||
$ ./purse.sh r github | cut -f 1 -d ' ' | awk 'NR==4{print $1}' | xclip
|
||||
./purse.sh b
|
||||
```
|
||||
|
||||
This script and encrypted `purse.enc` file can be publicly shared between trusted computers. For additional privacy, the recipient key ID is **not** included in GPG metadata.
|
||||
Restore an archive from backup:
|
||||
|
||||
See [drduh/config/gpg.conf](https://github.com/drduh/config/blob/master/gpg.conf) for additional GPG options.
|
||||
```console
|
||||
tar xvf purse*tar
|
||||
```
|
||||
|
||||
# Similar software
|
||||
# Configure
|
||||
|
||||
* [drduh/pwd.sh](https://github.com/drduh/pwd.sh)
|
||||
* [bndw/pick: command-line password manager for macOS and Linux](https://github.com/bndw/pick)
|
||||
* [Pass: the standard unix password manager](https://www.passwordstore.org/)
|
||||
* [anders/pwgen: generate passwords using OS X Security framework](https://github.com/anders/pwgen)
|
||||
* [caodonnell/passman.sh: a pwd.sh fork](https://github.com/caodonnell/passman.sh)
|
||||
Several customizable options and features are also available, and can be configured with environment variables, for example in the [shell rc](https://github.com/drduh/config/blob/master/zshrc) file:
|
||||
|
||||
Variable | Description | Default | Values
|
||||
-|-|-|-
|
||||
`PURSE_TIME` | seconds to clear password from clipboard/screen | `10` | any valid integer
|
||||
`PURSE_LEN` | default generated password length | `14` | any valid integer
|
||||
`PURSE_COPY` | copy password to clipboard before write | unset (disabled) | `1` or `true` to enable
|
||||
`PURSE_DAILY` | create daily backup archive on write | unset (disabled) | `1` or `true` to enable
|
||||
`PURSE_ENCIX` | encrypt index for additional privacy; 2 YubiKey touches will be required for separate decryption operations | unset (disabled) | `1` or `true` to enable
|
||||
`PURSE_COMMENT` | **unencrypted** comment to include in index and safe files | unset | any valid string
|
||||
`PURSE_CHARS` | character set for passwords | `[:alnum:]!?@#$%^&*();:+=` | any valid characters
|
||||
`PURSE_DEST` | password output destination, will set to `screen` without clipboard | `clipboard` | `clipboard` or `screen`
|
||||
`PURSE_ECHO` | character used to echo password input | `*` | any valid character
|
||||
`PURSE_SAFE` | safe directory name | `safe` | any valid string
|
||||
`PURSE_INDEX` | index file name | `purse.index` | any valid string
|
||||
`PURSE_BACKUP` | backup archive file name | `purse.$hostname.$today.tar` | any valid string
|
||||
|
||||
**Note** For additional privacy, the recipient key ID is **not** included in metadata (GnuPG `throw-keyids` option).
|
||||
|
||||
|
||||
|
||||
See [config/gpg.conf](https://github.com/drduh/config/blob/master/gpg.conf) for additional GnuPG options.
|
||||
|
|
370
purse.sh
370
purse.sh
|
@ -1,230 +1,316 @@
|
|||
#!/usr/bin/env bash
|
||||
# https://github.com/drduh/Purse
|
||||
|
||||
# https://github.com/drduh/Purse/blob/master/purse.sh
|
||||
#set -x # uncomment to debug
|
||||
set -o errtrace
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
umask 077
|
||||
export LC_ALL="C"
|
||||
|
||||
filter="$(command -v grep) -v -E"
|
||||
now="$(date +%s)"
|
||||
today="$(date +%F)"
|
||||
copy="$(command -v xclip || command -v pbcopy)"
|
||||
gpg="$(command -v gpg || command -v gpg2)"
|
||||
safe="${PURSE_SAFE:=purse.enc}"
|
||||
keyid="0xFF3E7D88647EBCDB"
|
||||
gpg_conf="${GNUPGHOME}/gpg.conf"
|
||||
|
||||
clip_dest="${PURSE_DEST:=clipboard}" # set to 'screen' to print to stdout
|
||||
clip_timeout="${PURSE_TIME:=10}" # seconds to clear clipboard/screen
|
||||
comment="${PURSE_COMMENT:=}" # *unencrypted* comment in files
|
||||
daily_backup="${PURSE_DAILY:=}" # daily backup archive on write
|
||||
encrypt_index="${PURSE_ENCIX:=}" # also keep index encrypted
|
||||
pass_copy="${PURSE_COPY:=}" # copy password before write
|
||||
pass_echo="${PURSE_ECHO:=*}" # show "*" when typing passwords
|
||||
pass_len="${PURSE_LEN:=14}" # default password length
|
||||
safe_dir="${PURSE_SAFE:=safe}" # safe directory name
|
||||
safe_ix="${PURSE_INDEX:=purse.index}" # index file name
|
||||
safe_backup="${PURSE_BACKUP:=purse.$(hostname).${today}.tar}"
|
||||
pass_chars="${PURSE_CHARS:='[:alnum:]!?@#$%^&*();:+='}"
|
||||
|
||||
trap cleanup EXIT INT TERM
|
||||
cleanup () {
|
||||
# "Lock" files on trapped exits.
|
||||
|
||||
ret=$?
|
||||
chmod -R 0000 \
|
||||
"${safe_dir}" "${safe_ix}" 2>/dev/null
|
||||
exit ${ret}
|
||||
}
|
||||
|
||||
fail () {
|
||||
# Print an error message and exit.
|
||||
# Print an error in red and exit.
|
||||
|
||||
printf "\n\n"
|
||||
tput setaf 1 1 1 ; echo "Error: ${1}" ; tput sgr0
|
||||
tput setaf 1 ; printf "\nERROR: %s\n" "${1}" ; tput sgr0
|
||||
exit 1
|
||||
}
|
||||
|
||||
warn () {
|
||||
# Print a warning in yellow.
|
||||
|
||||
tput setaf 3 ; printf "\nWARNING: %s\n" "${1}" ; tput sgr0
|
||||
}
|
||||
|
||||
get_pass () {
|
||||
# Prompt for a password.
|
||||
|
||||
password=""
|
||||
prompt="${1}"
|
||||
prompt=" ${1}"
|
||||
printf "\n"
|
||||
|
||||
while IFS= read -p "${prompt}" -r -s -n 1 char ; do
|
||||
if [[ ${char} == $'\0' ]] ; then
|
||||
break
|
||||
if [[ ${char} == $'\0' ]] ; then break
|
||||
elif [[ ${char} == $'\177' ]] ; then
|
||||
if [[ -z "${password}" ]] ; then
|
||||
prompt=""
|
||||
if [[ -z "${password}" ]] ; then prompt=""
|
||||
else
|
||||
prompt=$'\b \b'
|
||||
password="${password%?}"
|
||||
fi
|
||||
else
|
||||
prompt="*"
|
||||
prompt="${pass_echo}"
|
||||
password+="${char}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
decrypt () {
|
||||
# Decrypt with authorized GPG key.
|
||||
# Decrypt with GPG.
|
||||
|
||||
cat "${1}" | ${gpg} --armor --batch --decrypt 2>/dev/null
|
||||
cat "${1}" | \
|
||||
${gpg} --armor --batch --decrypt 2>/dev/null
|
||||
}
|
||||
|
||||
|
||||
encrypt () {
|
||||
# Encrypt to a recipient.
|
||||
# Encrypt to a group of hidden recipients.
|
||||
|
||||
${gpg} --encrypt --armor --batch --yes --throw-keyids \
|
||||
--recipient ${keyid} --output "${1}" "${2}"
|
||||
${gpg} --encrypt --armor --batch --yes \
|
||||
--hidden-recipient "purse_keygroup" \
|
||||
--throw-keyids --comment "${comment}" \
|
||||
--output "${1}" "${2}" 2>/dev/null
|
||||
}
|
||||
|
||||
|
||||
read_pass () {
|
||||
# Read a password from safe.
|
||||
|
||||
if [[ ! -s ${safe} ]] ; then fail "${safe} not found" ; fi
|
||||
if [[ ! -s "${safe_ix}" ]] ; then fail "${safe_ix} not found" ; fi
|
||||
|
||||
if [[ -z "${2+x}" ]] ; then read -r -p "
|
||||
Username (Enter for all): " username
|
||||
else
|
||||
username="${2}"
|
||||
while [[ -z "${username}" ]] ; do
|
||||
if [[ -z "${2+x}" ]] ; then read -r -p "
|
||||
Username: " username
|
||||
else username="${2}" ; fi
|
||||
done
|
||||
|
||||
if [[ -n "${encrypt_index}" ]] ; then prompt_key "index"
|
||||
spath=$(decrypt "${safe_ix}" | \
|
||||
grep -F "${username}" | tail -1 | cut -d ":" -f2) || \
|
||||
fail "Secret not available"
|
||||
else spath=$(grep -F "${username}" "${safe_ix}" | \
|
||||
tail -1 | cut -d ":" -f2)
|
||||
fi
|
||||
|
||||
if [[ -z "${username}" || "${username}" == "all" ]] ; then username="" ; fi
|
||||
|
||||
prompt_key
|
||||
decrypt ${safe} | grep -F " ${username}" || fail "Decryption failed"
|
||||
prompt_key "password"
|
||||
if [[ -s "${spath}" ]] ; then
|
||||
clip <(decrypt "${spath}" | head -1) || \
|
||||
fail "Failed to decrypt ${spath}"
|
||||
else fail "Secret not available"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
prompt_key () {
|
||||
# Print a message when key touch is needed.
|
||||
# Print a message if safe file exists.
|
||||
|
||||
if [[ -f "${safe}" ]] ; then
|
||||
printf "\n Touch key to decrypt safe ...\n\n"
|
||||
if [[ -f "${safe_ix}" ]] ; then
|
||||
printf "\n Touch key to access %s ...\n" "${1}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
gen_pass () {
|
||||
# Generate a password.
|
||||
# Generate a password from urandom.
|
||||
|
||||
len=20
|
||||
max=80
|
||||
if [[ -z "${3+x}" ]] ; then read -r -p "
|
||||
Password length (default: ${pass_len}): " length
|
||||
else length="${3}" ; fi
|
||||
|
||||
if [[ -z "${3+x}" ]] ; then read -p "
|
||||
Password length (default: ${len}, max: ${max}): " length
|
||||
else
|
||||
length="${3}"
|
||||
if [[ "${length}" =~ ^[0-9]+$ ]] ; then
|
||||
pass_len="${length}"
|
||||
fi
|
||||
|
||||
if [[ ${length} =~ ^[0-9]+$ ]] ; then len=${length} ; fi
|
||||
|
||||
# base64: 4 characters for every 3 bytes
|
||||
${gpg} --armor --gen-random 0 "$((${max} * 3/4))" | cut -c -"${len}"
|
||||
}
|
||||
|
||||
|
||||
write_pass () {
|
||||
# Write a password in safe.
|
||||
|
||||
# If no password (delete action), clear the entry by writing an empty line.
|
||||
if [[ -z "${userpass+x}" ]] ; then
|
||||
entry=" "
|
||||
else
|
||||
entry="${userpass} ${username}"
|
||||
fi
|
||||
|
||||
prompt_key
|
||||
|
||||
# If safe exists, decrypt it and filter out username, or bail on error.
|
||||
# If successful, append entry, or blank line.
|
||||
# Filter blank lines and previous timestamp, append fresh timestamp.
|
||||
# Finally, encrypt it all to a new safe file, or fail.
|
||||
# If successful, update to new safe file.
|
||||
( if [[ -f "${safe}" ]] ; then
|
||||
decrypt ${safe} | ${filter} " ${username}$" || return
|
||||
fi ; \
|
||||
echo "${entry}") | \
|
||||
(${filter} "^[[:space:]]*$|^mtime:[[:digit:]]+$";echo mtime:$(date +%s)) | \
|
||||
encrypt ${safe}.new - || fail "Write to safe failed"
|
||||
mv ${safe}{.new,}
|
||||
tr -dc "${pass_chars}" < /dev/urandom | \
|
||||
fold -w "${pass_len}" | head -1
|
||||
}
|
||||
|
||||
write_pass () {
|
||||
# Write a password and update the index.
|
||||
|
||||
new_entry () {
|
||||
# Prompt for new username and/or password.
|
||||
spath="${safe_dir}/$(tr -dc "[:lower:]" < /dev/urandom | \
|
||||
fold -w10 | head -1)"
|
||||
|
||||
if [[ -z "${2+x}" ]] ; then read -r -p "
|
||||
Username: " username
|
||||
else
|
||||
username="${2}"
|
||||
if [[ -n "${pass_copy}" ]] ; then
|
||||
clip <(printf '%s' "${userpass}")
|
||||
fi
|
||||
|
||||
if [[ -z "${3+x}" ]] ; then get_pass "
|
||||
Password for \"${username}\" (Enter to generate): "
|
||||
printf '%s\n' "${userpass}" | \
|
||||
encrypt "${spath}" - || \
|
||||
fail "Failed saving ${spath}"
|
||||
|
||||
if [[ -n "${encrypt_index}" ]] ; then
|
||||
prompt_key "index"
|
||||
|
||||
( if [[ -f "${safe_ix}" ]] ; then
|
||||
decrypt "${safe_ix}" || return ; fi
|
||||
printf "%s@%s:%s\n" "${username}" "${now}" "${spath}") | \
|
||||
encrypt "${safe_ix}.${now}" - && \
|
||||
mv "${safe_ix}.${now}" "${safe_ix}" || \
|
||||
fail "Failed saving ${safe_ix}.${now}"
|
||||
else
|
||||
printf "%s@%s:%s\n" \
|
||||
"${username}" "${now}" "${spath}" >> "${safe_ix}"
|
||||
fi
|
||||
}
|
||||
|
||||
list_entry () {
|
||||
# Decrypt the index to list entries.
|
||||
|
||||
if [[ ! -s "${safe_ix}" ]] ; then fail "${safe_ix} not found" ; fi
|
||||
|
||||
if [[ -n "${encrypt_index}" ]] ; then prompt_key "index"
|
||||
decrypt "${safe_ix}" || fail "${safe_ix} not available"
|
||||
else printf "\n" ; cat "${safe_ix}"
|
||||
fi
|
||||
}
|
||||
|
||||
backup () {
|
||||
# Archive index, safe and configuration.
|
||||
|
||||
if [[ ! -f ${safe_backup} ]] ; then
|
||||
if [[ -f "${safe_ix}" && -d "${safe_dir}" ]] ; then
|
||||
cp "${gpg_conf}" "gpg.conf.${today}"
|
||||
tar cf "${safe_backup}" "${safe_dir}" "${safe_ix}" \
|
||||
"${BASH_SOURCE}" "gpg.conf.${today}" && \
|
||||
printf "\nArchived %s\n" "${safe_backup}"
|
||||
rm -f "gpg.conf.${today}"
|
||||
else fail "Nothing to archive" ; fi
|
||||
else warn "${safe_backup} exists, skipping archive" ; fi
|
||||
}
|
||||
|
||||
clip () {
|
||||
# Use clipboard or stdout and clear after timeout.
|
||||
|
||||
if [[ "${clip_dest}" = "screen" ]] ; then
|
||||
printf '\n%s\n' "$(cat ${1})"
|
||||
else "${copy}" < "${1}" ; fi
|
||||
|
||||
printf "\n"
|
||||
while [[ "${clip_timeout}" -gt 0 ]] ; do
|
||||
printf "\r\033[K Password on %s! Clearing in %.d" \
|
||||
"${clip_dest}" "$((clip_timeout--))" ; sleep 1
|
||||
done
|
||||
printf "\r\033[K Clearing password from %s ..." "${clip_dest}"
|
||||
|
||||
if [[ "${clip_dest}" = "screen" ]] ; then clear
|
||||
else printf "\n" ; printf "" | "${copy}" ; fi
|
||||
}
|
||||
|
||||
setup_keygroup() {
|
||||
# Configure one or more recipients.
|
||||
|
||||
purse_keygroup="group purse_keygroup ="
|
||||
keyid=""
|
||||
recommend="$(${gpg} -K | grep "sec#" | \
|
||||
awk -F "/" '{print $2}' | cut -c-18 | tr "\n" " ")"
|
||||
|
||||
printf "\n Setting up keygroup ...\n
|
||||
Found recommended key IDs: %s\n
|
||||
Enter one or more key IDs, preferred one last\n" "${recommend}"
|
||||
|
||||
while [[ -z "${keyid}" ]] ; do read -r -p "
|
||||
Key ID or Enter to continue: " keyid
|
||||
if [[ -z "${keyid}" ]] ; then
|
||||
printf "%s\n" "${purse_keygroup}" >> "${gpg_conf}"
|
||||
break
|
||||
fi
|
||||
purse_keygroup="${purse_keygroup} ${keyid}"
|
||||
keyid=""
|
||||
done
|
||||
}
|
||||
|
||||
new_entry () {
|
||||
# Prompt for username and password.
|
||||
|
||||
while [[ -z "${username}" ]] ; do
|
||||
if [[ -z "${2+x}" ]] ; then read -r -p "
|
||||
Username: " username
|
||||
else username="${2}" ; fi
|
||||
done
|
||||
|
||||
if [[ -z "${3+x}" ]] ; then
|
||||
get_pass "Password for \"${username}\" (Enter to generate): "
|
||||
userpass="${password}"
|
||||
fi
|
||||
|
||||
printf "\n"
|
||||
|
||||
if [[ -z "${password}" ]] ; then userpass=$(gen_pass "$@")
|
||||
if [[ -z "${4+x}" || ! "${4}" =~ ^([qQ])$ ]] ; then
|
||||
printf "\n Password: ${userpass}\n"
|
||||
fi
|
||||
if [[ -z "${password}" ]] ; then
|
||||
userpass=$(gen_pass "$@")
|
||||
fi
|
||||
}
|
||||
|
||||
print_help () {
|
||||
# Print help text.
|
||||
|
||||
echo "
|
||||
Purse is a password manager shell script using GnuPG asymmetric encryption. It is recommended for use with Yubikey or similar hardware token. Purse can be used interactively or with one of the following options:
|
||||
|
||||
* 'r' to read a password
|
||||
printf """
|
||||
Purse is a Bash shell script to manage passwords with GnuPG asymmetric encryption. It is designed and recommended to be used with YubiKey as the secret key storage.\n
|
||||
Purse can be used interactively or by passing one of the following options:\n
|
||||
* 'w' to write a password
|
||||
* 'd' to delete a password
|
||||
* 'h' to print this help text
|
||||
|
||||
A username, password length and 'q' options can also be used.
|
||||
|
||||
Examples:
|
||||
|
||||
* Read all passwords:
|
||||
|
||||
./purse.sh r all
|
||||
|
||||
* Write a password for 'github':
|
||||
|
||||
./purse.sh w github
|
||||
|
||||
* Generate a 50 character password for 'github' and write:
|
||||
|
||||
./purse.sh w github 50
|
||||
|
||||
* Generate a password and write without displaying it:
|
||||
|
||||
./purse.sh w github 50 q
|
||||
|
||||
* Delete password for 'mail':
|
||||
|
||||
./purse.sh d mail
|
||||
|
||||
A password cannot be supplied as an argument, nor is used as one in the script, to prevent it from appearing in process listing or logs."
|
||||
* 'r' to read a password
|
||||
* 'l' to list passwords
|
||||
* 'b' to create an archive for backup\n
|
||||
Options can also be passed on the command line.\n
|
||||
* Create a 20-character password for userName:
|
||||
./purse.sh w userName 20\n
|
||||
* Read password for userName:
|
||||
./purse.sh r userName\n
|
||||
* Passwords are stored with an epoch timestamp for revision control. The most recent version is copied to clipboard on read. To list all passwords or read a specific version of a password:
|
||||
./purse.sh l
|
||||
./purse.sh r userName@1574723625\n
|
||||
* Create an archive for backup:
|
||||
./purse.sh b\n
|
||||
* Restore an archive from backup:
|
||||
tar xvf purse*tar\n"""
|
||||
}
|
||||
|
||||
if [[ -z "${gpg}" || ! -x "${gpg}" ]] ; then fail "GnuPG is not available" ; fi
|
||||
|
||||
if [[ -z ${gpg} && ! -x ${gpg} ]] ; then fail "GnuPG is not available" ; fi
|
||||
if [[ ! -f "${gpg_conf}" ]] ; then fail "GnuPG config is not available" ; fi
|
||||
|
||||
if [[ ! -d "${safe_dir}" ]] ; then mkdir -p "${safe_dir}" ; fi
|
||||
|
||||
chmod -R 0700 "${safe_dir}" "${safe_ix}" 2>/dev/null
|
||||
|
||||
if [[ -z "${copy}" || ! -x "${copy}" ]] ; then
|
||||
warn "Clipboard not available, passwords will print to screen/stdout!"
|
||||
clip_dest="screen"
|
||||
fi
|
||||
|
||||
username=""
|
||||
password=""
|
||||
|
||||
action=""
|
||||
if [[ -n "${1+x}" ]] ; then
|
||||
action="${1}"
|
||||
fi
|
||||
|
||||
while [[ -z "${action}" ]] ;
|
||||
do read -n 1 -p "
|
||||
Read, Write, or Delete password (or Help): " action
|
||||
if [[ -n "${1+x}" ]] ; then action="${1}" ; fi
|
||||
|
||||
while [[ -z "${action}" ]] ; do read -r -n 1 -p "
|
||||
Read or Write (or Help for more options): " action
|
||||
printf "\n"
|
||||
done
|
||||
|
||||
if [[ "${action}" =~ ^([hH])$ ]] ; then
|
||||
print_help
|
||||
if [[ "${action}" =~ ^([rR])$ ]] ; then read_pass "$@"
|
||||
elif [[ "${action}" =~ ^([wW])$ ]] ; then
|
||||
purse_keygroup="$(grep "group purse_keygroup" "${gpg_conf}")"
|
||||
if [[ -z "${purse_keygroup}" ]] ; then
|
||||
setup_keygroup
|
||||
fi
|
||||
printf "\n %s\n" "${purse_keygroup}"
|
||||
new_entry "$@"
|
||||
write_pass
|
||||
elif [[ "${action}" =~ ^([dD])$ ]] ; then
|
||||
if [[ -z "${2+x}" ]] ; then read -p "
|
||||
Username: " username
|
||||
else
|
||||
username="${2}"
|
||||
fi
|
||||
write_pass
|
||||
else
|
||||
read_pass "$@"
|
||||
fi
|
||||
if [[ -n "${daily_backup}" ]] ; then backup ; fi
|
||||
elif [[ "${action}" =~ ^([lL])$ ]] ; then list_entry
|
||||
elif [[ "${action}" =~ ^([bB])$ ]] ; then backup
|
||||
else print_help ; fi
|
||||
|
||||
printf "\n" ; tput setaf 2 2 2 ; echo "Done" ; tput sgr0
|
||||
tput setaf 2 ; printf "\nDone\n" ; tput sgr0
|
||||
|
|
Loading…
Reference in New Issue