Simple TOTP Bash Script
Using Two Factor Authentication (2FA) for services is a good idea. Most services will require you to have an Android or iOS smartphone and use Google Authenticator or similar apps to generate TOTP codes.
That’s fine if you have a smartphone. If you don’t have one you can often use SMS tokens instead. But SMS for 2FA is not recommended anymore. Also, what do you do if you don’t have a smartphone or mobile phone at all? I know of at least 2 people who need to use 2FA and don’t have a smartphone.
First thing I suggested to them: use Authy, there’s a Chrome plugin you can use instead of a smartphone. But it turns out, they seem to require you to have a smartphone. I guess for backup or security reasons.
Since both people have access to a Linux system, I figured I might as well find a Linux client that allows them to generate TOTPs. But all tools or scripts I could find seemed bloated. Until Emma Delescolle pointed me to oath-toolkit. Thanks!
With the included oathtool one can generate a TOTP:
$ oathtool --base32 --totp ONSWG4TFOQYTEMZUGU3DOOBZ
785263
Great. Now I only needed a way to securely store the secret. I figured a symmetrically encrypted file using GnuPG would probably do just fine:
$ echo ONSWG4TFOQYTEMZUGU3DOOBZ | gpg --symmetric --out ~/.secret-file
And loading that file isn’t hard either:
$ secret="$(gpg < ~/.secret-file)"
$ oathtool --base32 --totp "$secret"
785263
Great. Now I only needed to tie those things together in a small script that lets me add new secrets for various services and gives me a TOTP back when I need one:
#!/bin/bash
# Copyright (c) 2018, info AT markusholtermann DOT eu
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the <organization> nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -e
set -o pipefail
SOURCE_DIR=~/.config/2fa
function init() {
mkdir -p $SOURCE_DIR
chmod 0700 $SOURCE_DIR
if ! hash gpg 2>/dev/null ; then
echo "Please ensure that GnuPG is installed!"
exit 1
fi
if ! hash oathtool 2>/dev/null ; then
echo "Please ensure that oathtool is installed!"
exit 2
fi
}
function add_key() {
echo "Adding a new key"
if [ "x$1" != "x" ] ; then
identifier=$1
else
echo "What's the identifier?"
read -r identifier
fi
echo "What's the secret?"
read -r secret
echo "$secret" | gpg --quiet --symmetric --out "$SOURCE_DIR/$identifier"
}
function get_totp() {
if [ "x$1" != "x" ] ; then
identifier=$1
else
echo "What's the identifier?"
read -r identifier
fi
secret="$(gpg --quiet < "$SOURCE_DIR/$identifier")"
oathtool --base32 --totp "$secret"
}
function list() {
ls -1 "$SOURCE_DIR"
}
function help() {
echo "Setup a new TOTP account or generate a new TOTP token from an existing account."
echo
echo "Usage: totp.sh [--add|--list|--totp] [IDENTIFIER]"
echo
echo -e "--add Will ask for an identifier (i.e. 'google', 'slack', ...) and\\n" \
" then for the secret provided by the service provider."
echo -e "--list Will list all available identifiers."
echo -e "--totp Will ask for an identifier (i.e. 'google', 'slack', ...) and\\n" \
" then return the TOTP token."
}
init
case $1 in
--add)
add_key "$2"
;;
--list)
list
;;
--totp)
get_totp "$2"
;;
*)
help
esac
How do I use the tool? Let’s say we want to add 2FA to our Google account. You copy the 2FA key from Google’s website and add it:
$ totp.sh --add google
Adding a new key
What's the secret?
abcd efgh 1234 ijkl mnop 5678 90qr stuv
At this point you’ll be asked for a password and confirmation to secure the secret. Repeat the above with different identifiers for various services you want.
You can then list all available services / identifiers:
$ totp.sh --list
google
And lastly, you can get the TOTP back:
$ totp.sh --totp google
612027