env/bin/sekrit.sh

229 lines
4.1 KiB
Bash
Raw Normal View History

2019-12-04 11:52:17 +01:00
#!/bin/sh
# sekrit
2020-05-03 16:47:27 +02:00
# Written in 2018-2020 by Lucas
2019-12-04 11:52:17 +01:00
# CC0 1.0 Universal/Public domain - No rights reserved
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide. This software is distributed without any
# warranty. You should have received a copy of the CC0 Public Domain
# Dedication along with this software. If not, see
# <http://creativecommons.org/publicdomain/zero/1.0/>.
# sekrit is a relatively small shell script for writing and reading
# encrypted files, aimed to deal mostly with accounts credentials, but
# general enough to deal with any content. It can populate each key
# with random data or with fixed data, reading either command-line
# arguments or stdin.
#
# To be used as a password manager, it's recommended to pair it with
# xclip(1). For example, for username, password and optional second
# factor:
#
# sekrit get account/user | xclip -r -l 1 -sel clip -q
# sekrit get account/pass | xclip -r -l 1 -sel clip -q
2020-08-22 02:01:38 +02:00
# sekrit has account/2fa && sekrit get account/2fa |
# $program_for_totp | xclip -r -l 1 -sel clip -q
2019-12-04 11:52:17 +01:00
usage()
{
cat - <<. >&2
Usage:
${0##*/} add key [value ...]
${0##*/} cp [-k] key
2019-12-27 23:53:22 +01:00
${0##*/} gen [-l length] [chars]
${0##*/} get key
${0##*/} has key
${0##*/} ls [keys ...]
${0##*/} rm [-f] key [key ...]
2019-12-04 11:52:17 +01:00
If no value was provided on command line, add reads from stdin.
.
exit 1
}
err()
{
printf "%s: %s\n" "${0##*/}" "$*" >&2
exit 1
}
check_key()
{
2019-12-27 23:52:59 +01:00
case $1 in
*/ | /* | ./* | */./* | */. | ../* | */../* | */..)
2020-05-03 16:47:27 +02:00
err "$1: invalid key"
2019-12-27 23:52:59 +01:00
;;
esac
2019-12-04 11:52:17 +01:00
}
2020-12-02 04:40:50 +01:00
make_key_path()
{
check_key "$1" && printf "%s/%s.gpg" "$SEKRIT_DIR" "$1"
}
2019-12-04 11:52:17 +01:00
to_number()
{
printf "%u" "$*" 2>/dev/null
}
2020-12-02 04:40:50 +01:00
_sekrit_decrypt()
{
gpg2 -qd "$1"
}
2019-12-04 11:52:17 +01:00
sekrit_add()
{
2020-05-03 16:48:05 +02:00
[ $# -ge 1 ] && [ -n "$1" ] || usage
2019-12-04 11:52:17 +01:00
key=$1
shift
2020-12-02 04:40:50 +01:00
path=$(make_key_path "$key")
mkdir -p "${path%/*}"
2019-12-04 11:52:17 +01:00
[ ! -f "$path" ] || err "key $key already exists"
2019-12-05 11:58:10 +01:00
if [ $# -gt 0 ]; then
2020-05-03 16:47:27 +02:00
# use all additional parameters as a single string
2019-12-04 11:52:17 +01:00
printf "%s\n" "$*"
else
cat -
fi | gpg2 -qae -r "$SEKRIT_GPG_ID" >"$path"
2019-12-04 11:52:17 +01:00
# make it read-only
2020-12-02 04:40:50 +01:00
chmod -- 400 "$path"
2019-12-04 11:52:17 +01:00
}
sekrit_cp()
{
command -v xclip >/dev/null 2>&1 ||
err "xclip required for clipboard support"
OPTIND=1
rmlastnl=-rmlastnl
while getopts k flag; do
case "$flag" in
2020-08-22 02:01:38 +02:00
k) rmlastnl= ;;
*) usage ;;
esac
done
shift $((OPTIND - 1))
[ $# -eq 1 ] || usage
key=$1
2020-12-02 04:40:50 +01:00
path=$(make_key_path "$key")
[ -f "$path" ] || err "no data for key $key"
_sekrit_decrypt "$path" |
xclip $rmlastnl -loops 1 -quiet -selection clip 2>/dev/null
}
2019-12-04 11:52:17 +01:00
sekrit_gen()
{
OPTIND=1
len=43
2019-12-04 11:52:17 +01:00
while getopts l: flag; do
case "$flag" in
l) len=$(to_number "$OPTARG") ||
2020-05-03 16:47:27 +02:00
err "invalid password length"
2019-12-04 11:52:17 +01:00
;;
2020-08-22 02:01:38 +02:00
*) usage ;;
2019-12-04 11:52:17 +01:00
esac
done
2020-05-03 16:47:27 +02:00
shift $((OPTIND - 1))
2019-12-04 11:52:17 +01:00
[ $# -le 1 ] || usage
chars=+/0-9A-Za-z
if [ $# -eq 1 ]; then
[ -n "$1" ] || usage
chars=$1
fi
2020-08-22 02:01:38 +02:00
tr -cd -- "$chars" </dev/urandom |
dd bs=1 count="$len" 2>/dev/null &&
2019-12-04 11:52:17 +01:00
printf "\n"
}
sekrit_get()
{
[ $# -eq 1 ] || usage
key=$1
2020-12-02 04:40:50 +01:00
path=$(make_key_path "$key")
[ -f "$path" ] || err "no data for key $key"
_sekrit_decrypt "$path"
2019-12-04 11:52:17 +01:00
}
sekrit_has()
{
[ $# -eq 1 ] || usage
key=$1
2020-12-02 04:40:50 +01:00
path=$(make_key_path "$key")
[ -f "$path" ]
2019-12-04 11:52:17 +01:00
}
ls_key()
{
d=$SEKRIT_DIR$1
2020-08-22 02:01:38 +02:00
find "$d" -type f -name "*.gpg" |
sort |
2020-12-02 04:40:50 +01:00
sed -e "s#^$d##" -e 's#\.gpg$##'
2019-12-04 11:52:17 +01:00
}
sekrit_ls()
{
if [ $# -eq 0 ]; then
ls_key /
else
for key; do
printf "%s:\n" "$key"
2020-12-02 04:40:50 +01:00
ls_key "/$key/" | sed "s/^/ /"
2019-12-04 11:52:17 +01:00
printf "\n"
done
fi
}
sekrit_rm()
{
OPTIND=1
fflag=
while getopts f flag; do
case "$flag" in
f) fflag=-f ;;
*) usage ;;
esac
done
shift $((OPTIND - 1))
[ $# -ge 1 ] || usage
for key; do
path=$(make_key_path "$key")
if [ ! -f "$path" ]; then
printf "%s: no data for key %s\n" "${0##*/}" "$key" >&2
else
rm -i $fflag "$path"
fi
done
}
2019-12-04 11:52:17 +01:00
set -e
[ $# -ge 1 ] || usage
cmd=$1
shift
umask 077
: ${SEKRIT_DIR:=~/keep/sekrit}
mkdir -p "$SEKRIT_DIR"
[ -n "$SEKRIT_GPG_ID" ] || err "SEKRIT_GPG_ID is empty"
2019-12-04 11:52:17 +01:00
case "$cmd" in
2019-12-27 23:53:22 +01:00
add) sekrit_add "$@" ;;
cp) sekrit_cp "$@" ;;
2019-12-04 11:52:17 +01:00
gen) sekrit_gen "$@" ;;
get) sekrit_get "$@" ;;
has) sekrit_has "$@" ;;
ls) sekrit_ls "$@" ;;
rm) sekrit_rm "$@" ;;
2019-12-04 11:52:17 +01:00
*) usage ;;
esac