#!/bin/sh # sekrit # Written in 2018-2020 by Lucas # 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 # . # 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 # sekrit has account/2fa && sekrit get account/2fa \ # | $program_for_totp | xclip -r -l 1 -sel clip -q usage() { cat - <<. >&2 Usage: ${0##*/} add key [value ...] ${0##*/} cp [-k] key ${0##*/} gen [-l length] [chars] ${0##*/} get key ${0##*/} has key ${0##*/} ls [keys ...] 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() { case $1 in */ | /* | ./* | */./* | */. | ../* | */../* | */..) err "$1: invalid key" ;; esac } to_number() { printf "%u" "$*" 2>/dev/null } sekrit_add() { [ $# -ge 1 ] && [ -n "$1" ] || usage key=$1 check_key "$key" shift f=$SEKRIT_DIR/$key.gpg mkdir -p "${f%/*}" [ -f "$f" ] && err "key $key already exists" if [ $# -gt 0 ]; then # use all additional parameters as a single string printf "%s\n" "$*" else cat - fi | gpg2 -qae -r "$SEKRIT_GPG_ID" >"$f" # make it read-only chmod -- 400 "$f" } 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 k) rmlastnl= ;; *) usage ;; esac done shift $((OPTIND - 1)) [ $# -eq 1 ] || usage key=$1 sekrit_get "$key" | xclip $rmlastnl -loops 1 -quiet -selection clip 2>/dev/null } sekrit_gen() { len=43 OPTIND=1 while getopts l: flag; do case "$flag" in l) len=$(to_number "$OPTARG") || err "invalid password length" ;; *) usage ;; esac done shift $((OPTIND - 1)) [ $# -le 1 ] || usage chars=+/0-9A-Za-z if [ $# -eq 1 ]; then [ -n "$1" ] || usage chars=$1 fi tr -cd -- "$chars" /dev/null && printf "\n" } sekrit_get() { [ $# -eq 1 ] || usage key=$1 check_key "$key" shift f=$SEKRIT_DIR/$key.gpg [ -f "$f" ] || err "no data for key $key" gpg2 -qd "$f" } sekrit_has() { [ $# -eq 1 ] || usage key=$1 check_key "$key" shift [ -f "$SEKRIT_DIR/$key.gpg" ] } ls_key() { d=$SEKRIT_DIR$1 find "$d" -type f -name "*.gpg" | sort | sed -e "s#^$d##" -e "s#\.gpg\$##" } sekrit_ls() { if [ $# -eq 0 ]; then ls_key / else for key; do printf "%s:\n" "$key" ls_key /"$key"/ | sed "s/^/ /" printf "\n" done fi } set -e [ $# -ge 1 ] || usage cmd=$1 shift umask 077 : ${SEKRIT_DIR:=~/keep/sekrit} : ${SEKRIT_GPG_ID:=myself} mkdir -p "$SEKRIT_DIR" case "$cmd" in add) sekrit_add "$@" ;; cp) sekrit_cp "$@" ;; gen) sekrit_gen "$@" ;; get) sekrit_get "$@" ;; has) sekrit_has "$@" ;; ls) sekrit_ls "$@" ;; *) usage ;; esac