Skip to content

Instantly share code, notes, and snippets.

@hateshape
Created January 18, 2024 21:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hateshape/ef33ec245a9bebffacca8e880526361e to your computer and use it in GitHub Desktop.
Save hateshape/ef33ec245a9bebffacca8e880526361e to your computer and use it in GitHub Desktop.
OTP to your clipboard via CLI shenanigans and meh codes
#!/bin/bash
################################## INFOS ##################################
# Depends on exported otpauth-migration://offline from Google authenticator
# dependencies: expect oathtool otp_export python3-protobuf python3-urllib3 xclip zbarimg
# shellcheck disable=SC1087 disable=SC2128
###########################################################################
addkeys() {
if [[ -s "$OTPEXPORTTMP" && -n "$OTPEXPORTTMP" ]]; then
OTPEXPORT=$(zbarimg -q "$OTPEXPORTTMP" | sed 's#QR-Code:##g')
elif [[ ! -s "$OTPEXPORTTMP" && -n "$OTPEXPORTTMP" ]]; then
OTPEXPORT="$OTPEXPORTTMP"
else
die "something something key add error. Still your fault though."
fi
TMPOTPEXPORT="/tmp/.temp_otp_export"
TMPOTPEXPORTJSON="/tmp/.temp_otp_export.json"
if [[ -n "$OTPEXPORT" ]]; then
python3 "$TOOLS/otp_export/parse.py" "$OTPEXPORT" >"$TMPOTPEXPORT"
NAMECOUNT=$(grep -c "name:" "$TMPOTPEXPORT")
SECRETCOUNT=$(grep -c "otpauth:" "$TMPOTPEXPORT")
if [[ "$NAMECOUNT" == $SECRETCOUNT ]]; then
echo
jq -n -R -c '
reduce (inputs | select(length > 0)) as $item ([];
if ($item | index("otpauth")) then
. + [ {otpauth: $item | gsub("^ +otpauth: +"; "")} ]
elif ($item | index("name")) then
.[-1] += {name: $item | gsub("^ +name: +"; "")}
elif ($item | index("secret")) then
.[-1] += {secret: $item | gsub("^ +secret: +"; "")}
else
.
end
)
' "$TMPOTPEXPORT" | jq "{otp_parameters: .}" | jq 'walk(if type == "string" then gsub("^[[:space:]]+"; "") else . end)' >"$TMPOTPEXPORTJSON"
fi
fi
for ((i = 0; i < $(jq ".otp_parameters" "$TMPOTPEXPORTJSON" | jq length); i++)); do
TMPNAME=$(jq -r ".otp_parameters[$i].name" "$TMPOTPEXPORTJSON")
TMPSECRET=$(jq -r ".otp_parameters[$i].otpauth" "$TMPOTPEXPORTJSON" | cut -d\= -f2)
echo "$TMPSECRET" | gpg --quiet --symmetric --out "$OTPDIR/$TMPNAME" --batch --passphrase "$PASSPHRASE"
unset TMPNAME TMPSECRET
done
if [[ -s "$TMPOTPEXPORT" ]]; then rm "$TMPOTPEXPORT"; fi
if [[ -s "$TMPOTPEXPORTJSON" ]]; then rm "$TMPOTPEXPORTJSON"; fi
}
keyme() {
SECRET_NAME=$1
READ_SECRET_SECRET="$(gpg --passphrase "$PASSPHRASE" --batch --quiet <"$OTPDIR/$SECRET_NAME" | sed 's#&issuer##g')"
oathtool --base32 --totp "$READ_SECRET_SECRET"
}
listkey() {
if [[ -d "$OTPDIR" ]]; then
ls -1 "$OTPDIR"
elif [[ ! -d "$OTPDIR" ]]; then
mkdir -p "$OTPDIR"
chmod 0700 "$OTPDIR"
ls -1 "$OTPDIR"
fi
}
toolsexist() {
APTTOOLSNEEDED=""
which expect &>/dev/null || APTTOOLSNEEDED+="expect,"
which oathtool &>/dev/null || APTTOOLSNEEDED+="oathtool,"
which zbarimg &>/dev/null || APTTOOLSNEEDED+="xclip,"
which zbarimg &>/dev/null || APTTOOLSNEEDED+="zbarimg,"
for PYTHONMODULE in python3-protobuf python3-urllib3; do
python3 -c "import $PYTHONMODULE" &>/dev/null
PYTHONMODULEEXISTS=$(echo $?)
if [[ "$PYTHONMODULEEXISTS" == 0 ]]; then
APTTOOLSNEEDED+="$PYTHONMODULE,"
fi
unset PYTHONMODULEEXISTS
done
if [[ ! "$APTTOOLSNEEDED" == "" ]]; then
APTTOOLSNEEDED=$(echo $APTTOOLSNEEDED | awk 'BEGIN{FS=OFS=","}{NF--; print}')
echo -e "${YELLOWYELLOW}[ERROR]${WHITEWHITE} Dependencies Not Met!\n${YELLOWYELLOW}[ERROR]${WHITEWHITE} You cant do anything right, can you? If you need tools to run a script, maybe install them first, mmmmm'k? Until you have the tools, be gone${RESET}"
echo -e "${YELLOWYELLOW}[ERROR]${WHITEWHITE} Tools Needed:${ORANGEORANGE} $APTTOOLSNEEDED ${RESET}"
echo
exit
fi
}
filesexist() {
FILESNEEDED=""
# Script Specific Files
[[ -f "$TOOLS/otp_export/parse.py" ]] || FILESNEEDED+="otp_export§"
if [[ ! "$FILESNEEDED" == "" ]]; then
FILESNEEDED=$(echo $FILESNEEDED | awk 'BEGIN{FS=OFS="§"}{NF--; print}')
echo -e "${YELLOWYELLOW}[ERROR]${WHITEWHITE} Dependencies Not Met!\n${YELLOWYELLOW}[ERROR]${WHITEWHITE} You cant do anything right, can you? If you need tools to run a script, maybe install them first, mmmmm'k? Until you have the tools, be gone${RESET}"
echo -e "${YELLOWYELLOW}[ERROR]${WHITEWHITE} Files Needed:${ORANGEORANGE} $FILESNEEDED ${RESET}"
echo -e "${WHITEWHITE} => git clone https://github.com/qistoph/otp_export $TOOLS/otp_export${RESET}"
echo
exit
fi
}
howitdone() {
echo -e "${LEVEL1} Usage:${LEVEL2} $FILEBASENAME${LEVEL3} [-h]${RESET}"
echo -e "\n${LEVEL3} Script options${RESET}"
SCRIPTOPTIONS=$(
cat <<EOF
${ORANGEORANGE}-a, --add-key ${RESET}Add new key otpauth-migration://offline?data=XXX]
${ORANGEORANGE}-g, --get-totp ${RESET}Get OTP key by name
${ORANGEORANGE}-l, --list-otp ${RESET}List existing keys
${ORANGEORANGE}-qr, --qr-code ${RESET}Use QR code image [/path/to/qrcode]
EOF
)
echo -e "$SCRIPTOPTIONS"
echo -e "\n${RESET} Helpful options${RESET}"
HELPFULOPTIONS=$(
cat <<EOF
${ORANGEORANGE}-h, --help ${RESET}Print this help and exit
${ORANGEORANGE}-v, --verbose ${RESET}Script Debug
EOF
)
echo -e "$HELPFULOPTIONS\n"
exit
}
die() {
local msg=$1
local code=${2-1} # default exit status 1
echo -e "${ORANGEORANGE}We got us an error. It's your bad no doubt ... try again or just quit please${RESET}"
echo -e "${ORANGEORANGE}If you care, error information: ${WHITEWHITE}$msg${RESET}"
exit "$code"
}
parse_params() {
# default values of variables set from params
# Exporting GAUTH Secrets: https://support.google.com/accounts/answer/1066447?hl=en&co=GENIE.Platform%3DAndroid
PASSPHRASE='IF-YOU-SMART-CHANGEME-CHANGEME'
OTPDIR="$HOME/.config/otp"
TOOLS="/opt/tools"
FILEBASENAME=$(basename "${BASH_SOURCE[0]}")
# Look-n-Feels
RESET='\033[00m'
ORANGEORANGE='\e[38;5;209m'
YELLOWYELLOW='\e[38;5;179m'
WHITEWHITE='\e[38;5;15m'
export RESET ORANGEORANGE
if [[ "$#" -eq 0 ]]; then
howitdone
exit
fi
while :; do
case "${1-}" in
-h | --help) howitdone ;;
-v | --verbose) set -x ;;
-a | --add-key)
OTPEXPORTTMP="${2-}"
shift
;;
-g | --get-totp)
KEYNAME="${2-}"
shift
;;
-l | --list-otp)
listkey
;;
-qr | --qr-code)
OTPEXPORTTMP="${2-}"
shift
;;
-?*) die "Unknown option: $1" ;;
*) break ;;
esac
shift
done
args=("$@")
return 0
}
parse_params "$@"
toolsexist
filesexist
mkdir -p "$OTPDIR"
if [[ -n "$OTPEXPORTTMP" ]]; then
addkeys $OTPEXPORTTMP
elif [[ -n "$KEYNAME" ]]; then
KEYNAME=$(echo "$KEYNAME")
keyme "$KEYNAME" | xargs echo -n | xclip -selection clipboard
elif [[ -n "$OTPEXPORTTMP" ]]; then
listkey
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment