Skip to content

Instantly share code, notes, and snippets.

@teamday
Created May 3, 2019 22:33
Show Gist options
  • Save teamday/7a6adeec3d4cf8c047c49703f7fbd1fd to your computer and use it in GitHub Desktop.
Save teamday/7a6adeec3d4cf8c047c49703f7fbd1fd to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
#hashapass: implement the algorithm from hashapass.com as a shell script
#nick@kousu.ca, Sep 8, 2013 -- 2015
#public domain <http://creativecommons.org/publicdomain/zero/1.0/>
#usage: hashapass [-l] [-s] [parameter]
# requirements:
# bash(?)
# openssl
# zenity [OPTIONAL] (for GUI interface)
# xclip [OPTIONAL]
#
# [ ] don't use zenity; instead use `gksu -p`; alternately, patch zenity to do screen grabbing during --password dialogs
# [ ] read master password from
# [ ] a keyring
# [ ] a file
# [x] stdin (UNSAFE, depending on how the user does it)
# [ ] on the command line (UNSAFE).
# then you could log in, unlocked your password hashes, and not actually have to type your master password anywhere.
# [-] implement variant algorithms, e.g. hmac-sha256, hmac-sha3, hmac-ripemd160. See: https://github.com/kousu/hashapass/pull/1
# [-] bulk hashing (tho why you would want this I don't know)
# [ ] clear the clipboard automatically, suggestion from @matlink: https://github.com/matlink/hashapass/commit/c6950c032d440c2ba04a3f19545b4707c6ce50c6
# [ ] Normalize on spaces (not tabs)
# [ ] i18n
# [x] initialize all my variables so that the environment can't confuse me
# [ ] Figure out how to distinguish between being run from the GUI (e.g. dmenu, Alt-F2 in Gnome, or a .desktop file) and having stdin piped to us
# In the stdin case, give an error, since by design we refuse to read passwords non-interactively
# [ ] *don't* write the result to stdout when directly called from the GUI
usage() {
echo "Usage:" "hashapass [-l] [-s] [parameter]"
}
unset USAGE #initialize... because we don't start clean
unset SHOW
unset SSHOW
unset LONG
#there is getopt and there is getopts. fml.
while getopts "hsl" opt; do
#echo "currently parsing argument '$opt'"
case "$opt" in
h)
USAGE=true;;
s)
#echo "SHOW turned on"
SHOW=true;;
l)
#echo "LONG turned on"
LONG=true;;
*)
#echo "Unknown option '$opt'";
exit -1;;
esac
done
shift $((OPTIND-1)) #eat everything
if ! which xclip 1>/dev/null; then #check if we can use xclip
echo "Warning: xclip not found in \$PATH. You should install xclip so that we do not need to display your password openly." >/dev/stderr
SHOW=true;
fi;
if [ ! $DISPLAY ]; then #check if we can use the clipboard, which only works if X is running
SHOW=true;
fi
if [ $USAGE ]; then
usage;
exit 0;
elif [ $# -gt 1 ]; then
echo -n "Too many parameters. "
usage;
exit -1;
elif [ $# -eq 1 ]; then
parameter=$1
fi
# figure out if we are at an interative GUI
# (if not, we're either at an interactive CLI or inside a script)
unset GUI
if ! tty -s && [ $DISPLAY ]; then #XXX this is *wrong*. It cannot distinguish between running inside of a script that was run from the GUI and running from the GUI directly.
GUI=true # it can tell apart "are we at a terminal" vs "were we from Alt-F2", though.
# I can't think of what to check. there's a bunch of environment vars but they don't change (except for SHLVL, but that's bash-specific and also there's no guarantee the level of the baseline)
# the only obvious change is the file descriptors: if we're being used inside a script then they are necessarily wired to a pipe
# but i also maybe *want*
# argh, why does gnome have to wire shit to sockets for no reason? fuck
fi
#if [ $GUI ]; then zenity --info --text "Interactive GUI Mode" fi #DEBUG
if [ -z $GUI ]; then
# we're on the command line, use `read`
if [ -z "$parameter" ]; then
read -p "[hashapass] Parameter: " parameter;
fi
read -s -p "[hashapass] Master Password: " password
echo >/dev/stderr #add a newline after the nonechoing password input above
else
if which zenity >/dev/null && [ $DISPLAY ]; then
# we're in a GUI (e.g. dmenu, a .desktop button or via Gnome/KDE's Alt-F2): use `zenity`
if [ -z "$parameter" ]; then
if ! parameter=$(zenity --entry --title "hashapass" --text "Parameter: "); then
exit 1;
fi
fi
if ! password=$(zenity --password --title "hashapass" --text "Master Password: "); then
exit 1;
fi;
else
# see TODO above about this section
# (in short: this *should* trigger when the user tries to pipe in to us;
# instead it triggers only when they pipe into us when not running X or if missing zenity)
echo "hashapass will not read passwords non-interactively."
# (that would defeat the purpose of hashapass, afterall; if the user
# is that insistent, they can explicitly save to a plain text file)
exit 1;
fi
fi
# refuse to use the empty password, which would be a giant security risk
# conveniently, this makes "no input" in GUI mode the same as "cancel"
if [ -z $password ]; then
exit 1
fi
hashapass() {
#from http://hashapass.com/en/cmd.html
parameter=$1
password=$2
hashed_pass=$(echo -n $parameter \
| openssl dgst -sha1 -binary -hmac $password \
| openssl enc -base64)
if [ $LONG ]; then
echo $hashed_pass
else
echo $hashed_pass | cut -c 1-9
fi
}
result=$(hashapass $parameter $password)
#TODO: check what this does if xclip is missing
echo -n $result | xclip -selection clipboard -i >/dev/null # dumping xclip's stdout to the bitbucket works around xclip's failure to properly daemonize
# *when run in a $() subshell*, xclip inherits the pipe the parent is reading values off and thus hangs the process.
# proper patch in review at https://sourceforge.net/p/xclip/patches/9/
# if we were told to -s(how):
# if stdout is interactive: print, for sure, since yes
# else:
# if GUI is interactive: zenity
# else : print to stdout (assume we're in a pipe)
# note that this tree *only outputs once*, so information doesn't leak (well, except for xclip, of course)
if [ $SHOW ]; then
if [ -z $GUI ]; then
# neither the GUI nor stdout is interactive, but we were told to -s(how):
# so trust the user, and print to stdout
echo $result
else
echo $result #XXX dirty patch: since I don't know how to define "GUI" properly, *leak information* for the sake of making scripting work
zenity --info --text "$result" --title "hashapass: Hashed Password"
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment