aboutsummaryrefslogtreecommitdiff
path: root/dpw-age
diff options
context:
space:
mode:
authorMitchell Riedstra <mitch@riedstra.dev>2021-10-26 22:24:57 -0400
committerMitchell Riedstra <mitch@riedstra.dev>2021-10-26 22:24:57 -0400
commitfefc6c24ddf29bb29cb7d4c2713a4fde9dab0e25 (patch)
tree92936b7c1e56272a75905f2fb7fc8c7f89099ae0 /dpw-age
parent34d76d7d76f7554f846e59b62e4d21d76a85d970 (diff)
downloaddpw-fefc6c24ddf29bb29cb7d4c2713a4fde9dab0e25.tar.gz
dpw-fefc6c24ddf29bb29cb7d4c2713a4fde9dab0e25.tar.xz
Add 'age' backend. Init functions.
Diffstat (limited to 'dpw-age')
-rwxr-xr-xdpw-age223
1 files changed, 223 insertions, 0 deletions
diff --git a/dpw-age b/dpw-age
new file mode 100755
index 0000000..1d98303
--- /dev/null
+++ b/dpw-age
@@ -0,0 +1,223 @@
+#!/bin/sh
+# Copyright 2021 Mitchell Riedstra
+#
+# Permission to use, copy, modify, and/or distribute this software for any purpose
+# with or without fee is hereby granted, provided that the above copyright notice
+# and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
+# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+# THIS SOFTWARE.
+#
+# Example storage plugin that wraps 'age' into a password manager.
+# https://github.com/FiloSottile/age
+set -e
+
+UMASK="${PASSWORD_STORE_UMASK:-077}"
+umask "$UMASK"
+
+DPW_AGE_DIR="${DPW_AGE_DIR:-$HOME/.dpw-age}"
+DPW_AGE_KEY="${DPW_AGE_KEY:-$HOME/.dpw-age-key}"
+
+USE_GIT=0
+[ -e "${DPW_AGE_DIR}/.git" ] && USE_GIT=1
+
+# Helper functions
+
+_git_commit() {
+ [ $USE_GIT -eq 0 ] && return
+ cd "${DPW_AGE_DIR}"
+ git add --all
+ git commit -am "DPW Auto-commit: $1"
+}
+
+_set_age_recipients() {
+if [ -n "$DPW_AGE_RECIPIENTS" ] ; then
+ DPW_AGE_RECIPIENTS="$(echo "$DPW_AGE_RECIPIENTS" | tr ' ' '\n' \
+ | sed -e's/^/ -r/' | tr '\n' ' ')"
+ return
+fi
+_pth="$1"; shift
+
+_pth="${DPW_AGE_DIR}/$_pth"
+
+
+id_file="$(dirname "${_pth}")/.recipients"
+while true ; do
+
+ # Break if id_file is above our password store directory
+ case $id_file in
+ ${DPW_AGE_DIR}*) ;;
+ *) break ;;
+ esac
+
+ if [ -e "$id_file" ] ; then
+ keys=""
+ while read -r key ; do
+ if [ -z "$key" ] ; then continue ; fi
+ keys="$keys -r $key"
+ done < "$id_file"
+
+ export DPW_AGE_RECIPIENTS="$keys"
+ return
+ fi
+
+ # Pop this up a directory level for the next time 'round
+ id_file="$(dirname "$id_file")"
+ id_file="$(dirname "$id_file")/.recipients"
+done
+
+echo "No '.recipients' files found, is '$DPW_AGE_DIR' initialized?"
+exit 1
+}
+
+
+# Interface
+
+show() {
+pth="$1"; shift
+#shellcheck disable=SC2086
+exec age -i "${DPW_AGE_KEY}" -d < "${DPW_AGE_DIR}/${pth}.age"
+}
+
+
+insert() {
+pth="$1"; shift
+_set_age_recipients "$pth"
+mkdir -p "$DPW_AGE_DIR/$(dirname "$pth")"
+#shellcheck disable=SC2086
+age -e ${DPW_AGE_RECIPIENTS} \
+ > "${DPW_AGE_DIR}/${pth}.age"
+_git_commit "Insert: $pth"
+}
+
+list() {
+find "$DPW_AGE_DIR/$1" -type f -iname "*.age" \
+ | while read -r line ; do
+ line="${line##$DPW_AGE_DIR}"
+ line="${line#/}"
+ line="${line#/}"
+ line="${line%.age}"
+ echo "$line"
+ done
+}
+
+remove() {
+cd "$DPW_AGE_DIR"
+recursive=
+force=
+while [ $# -gt 0 ] ; do case $1 in
+ -r) recursive="-r" ; shift ;;
+ -f) force="-f" ; shift ;;
+ -rf|-fr) recursive="-r" ; force="-f" ; shift ;;
+ *) break ;;
+esac ; done
+
+files=
+for fn in "$@" ; do
+ if [ -e "${fn}.age" ] ; then
+ files="${fn}.age "
+ else
+ files="$fn "
+ fi
+done
+
+#shellcheck disable=SC2086
+rm $recursive $force $files
+_git_commit "Remove: $*"
+}
+
+_init_help() {
+cat <<EOF
+Usage: $0 [--no-git] [--gen-key] [--[no-]pass] <recipients>...
+
+Git is the default.
+
+Gen key will generate a passphrase protected private key for you and place it in
+\$DPW_AGE_KEY ($DPW_AGE_KEY) and the public side will be '.pub'
+
+No pass disables the default for a passphrase protected key file
+
+The key will automatically be added to the recipients list.
+
+Multiple recipients may be specified
+
+See: https://github.com/FiloSottile/age for more information on 'age' itself.
+EOF
+exit 1
+}
+
+_init() {
+USE_GIT=1
+if [ -d "${DPW_AGE_DIR}" ] ; then
+ echo "Cannot init new password store, one exists"
+ exit 1
+fi
+
+recipients=
+genkey=1
+passphrase=1
+while [ $# -gt 0 ] ; do case $1 in
+ --no-git) USE_GIT=0 ; shift ;;
+ --gen-key) genkey=1; shift ;;
+ --pass) passphrase=1; shift ;;
+ --no-pass) passphrase=0; shift ;;
+ -r|--recipient) recipients="$recipients:$2"; shift ; shift ;;
+ *) _init_help ;;
+esac ; done
+
+[ $genkey -eq 1 ] && [ -e "${DPW_AGE_KEY}" ] \
+ && echo "Cannot continue, \"${DPW_AGE_KEY}\" already exists" && exit
+
+echo "genkey: $genkey passphrase: $passphrase"
+
+if [ $genkey -eq 1 ] && [ $passphrase -eq 1 ] ; then
+ touch "${DPW_AGE_KEY}.pub" # Let the user's UMASK bet set
+ umask 077 # but override it for the key
+ age-keygen 2>"${DPW_AGE_KEY}.pub" | age -p > "${DPW_AGE_KEY}" 2>&2
+ sed -i.bak -e's/^Public key: //g' "${DPW_AGE_KEY}.pub"
+ rm "${DPW_AGE_KEY}.pub.bak"
+ umask "$UMASK"
+elif [ $genkey -eq 1 ] ; then
+ touch "${DPW_AGE_KEY}.pub" # Let the user's UMASK bet set
+ umask 077 # but override it for the key
+ age-keygen >"${DPW_AGE_KEY}"
+ sed -ne's/^# public key: //gp' < "${DPW_AGE_KEY}" > "${DPW_AGE_KEY}.pub"
+ umask "$UMASK"
+fi
+
+mkdir -p "${DPW_AGE_DIR}"
+cd "${DPW_AGE_DIR}"
+
+if [ $USE_GIT -eq 1 ] ; then
+ git init
+ cat >> .git/config <<EOF
+[diff "age"]
+ binary = true
+ textconv = age --decrypt -i "${DPW_AGE_KEY}"
+EOF
+
+ echo "*.age diff=age" >> .gitattributes
+fi
+
+cat < "${DPW_AGE_KEY}.pub" > "${DPW_AGE_DIR}/.recipients"
+echo "$recipients" | tr ':' '\n' >> "${DPW_AGE_DIR}/.recipients"
+
+_git_commit
+
+echo "Age Password store initialized in ${DPW_AGE_DIR}"
+}
+
+act="$1"; shift
+case $act in
+ show) show "$@" ;;
+ list) list "$@" ;;
+ insert) insert "$@" ;;
+ rm) remove "$@" ;;
+ init) _init "$@" ;;
+ *) echo "Bad command $act"; exit 1; ;;
+esac