• Stars
    star
    1,299
  • Rank 36,217 (Top 0.8 %)
  • Language
    Python
  • License
    MIT License
  • Created over 5 years ago
  • Updated 10 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Minimal TOTP generator in 20 lines of Python

MinTOTP

MinTOTP is a minimal TOTP generator written in Python.

View Source PyPI Package PyPI Hits MIT License

Contents

Introduction

TOTP stands for Time-Based One-Time Password. Many websites and services require two-factor authentication (2FA) or multi-factor authentication (MFA) where the user is required to present two or more pieces of evidence:

  • Something only the user knows, e.g., password, passphrase, etc.
  • Something only the user has, e.g., hardware token, mobile phone, etc.
  • Something only the user is, e.g., biometrics.

A TOTP value serves as the second factor, i.e., it proves that the user is in possession of a device (e.g., mobile phone) that contains a TOTP secret key from which the TOTP value is generated. Usually the service provider that provides a user's account also issues a secret key encoded either as a Base32 string or as a QR code. This secret key is added to an authenticator app (e.g., Google Authenticator) on a mobile device. The app can then generate TOTP values based on the current time. By default, it generates a new TOTP value every 30 seconds.

MinTOTP is a Python tool that can be used to generate TOTP values from a secret key. Additionally, it exposes its functionality as module-level functions for Python developers. It can be used on any system with Python 3.4 or later installed on it.

Source Code

At the heart of the TOTP algorithm lies the HOTP algorithm. HOTP stands for HMAC-based One-Time Password. HMAC stands for Hash-based Message Authentication Code. Here are the relevant RFCs to learn more about these algorithms:

  • RFC 2104: HMAC: Keyed-Hashing for Message Authentication
  • RFC 4226: HOTP: An HMAC-Based One-Time Password Algorithm
  • RFC 6238: TOTP: Time-Based One-Time Password Algorithm

The source code in mintotp.py generates TOTP values from a secret key and current time. It's just 30 lines of code (actually 20 lines if we ignore the shebang and blank lines). There are no comments in the code, so a brief description of the code is presented in this section. Here is the entire code presented once again for convenience:

#!/usr/bin/env python3

import base64
import hmac
import struct
import sys
import time


def hotp(key, counter, digits=6, digest='sha1'):
    key = base64.b32decode(key.upper() + '=' * ((8 - len(key)) % 8))
    counter = struct.pack('>Q', counter)
    mac = hmac.new(key, counter, digest).digest()
    offset = mac[-1] & 0x0f
    binary = struct.unpack('>L', mac[offset:offset+4])[0] & 0x7fffffff
    return str(binary)[-digits:].zfill(digits)


def totp(key, time_step=30, digits=6, digest='sha1'):
    return hotp(key, int(time.time() / time_step), digits, digest)


def main():
    args = [int(x) if x.isdigit() else x for x in sys.argv[1:]]
    for key in sys.stdin:
        print(totp(key.strip(), *args))


if __name__ == '__main__':
    main()

In the code above, we use the hmac module available in the Python standard library to implement HOTP. The implementation can be found in the hotp() function. It is a pretty straightforward implementation of RFC 2104: Section 5: HOTP Algorithm. It takes a Base32-encoded secret key and a counter as input. It returns a 6-digit HOTP value as output.

The totp() function implements the TOTP algorithm. It is a thin wrapper around the HOTP algorithm. The TOTP value is obtained by invoking the HOTP function with the secret key and the number of time intervals (30 second intervals by default) that have elapsed since Unix epoch (1970-01-01 00:00:00 UTC).

Install

MinTOTP requires Python 3.4 or later. If Python 3.4 or later is present on your system, follow one of the two sections below to get MinTOTP on your system.

From PyPI

If you want to install the MinTOTP package from PyPI as a Python module on your system, then follow the steps provided below. Doing so makes MinTOTP available as the mintotp command that you can run on the terminal. A module named mintotp also becomes available that you can import in your own Python code.

  1. Enter the following command to install MinTOTP on your system:

    pip3 install mintotp
  2. Test that MinTOTP works fine as a command:

    mintotp <<< ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS

    A 6-digit TOTP value should appear as the output.

  3. Test that MinTOTP can be used as a library module:

    $ python3
    >>> import mintotp
    >>> mintotp.totp('ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS')
    >>> mintotp.hotp('ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS', 42)

    The totp() function call should return a 6-digit TOTP value based on the current time. The hotp() call should return the following HOTP value: 626854.

From GitHub

If you do not want to install MinTOTP to your system as a command but you want to work with the mintotp.py source file directly clone the GitHub repository of this project.

  1. Clone GitHub repository of this project and enter its top-level directory.

    git clone https://github.com/susam/mintotp.git
    cd mintotp
  2. Test that mintotp.py works fine:

    python3 mintotp.py <<< ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS

    A 6-digit TOTP value should appear as the output.

  3. Test that mintotp.py can be imported as a module:

    $ python3
    >>> import mintotp
    >>> mintotp.totp('ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS')
    >>> mintotp.hotp('ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS', 42)

    The totp() function call should return a 6-digit TOTP value based on the current time. The hotp() call should return the following HOTP value: 626854.

All examples provided in the sections below assume that MinTOTP has been installed from PyPI. If you choose to use MinTOTP from GitHub instead, replace all occurrences of mintotp in the example commands below with python3 mintotp.py.

Get Started

This section presents a few examples to quickly get started with MinTOTP.

Note that this section uses a few example secret keys and QR codes. They are merely examples that come with this project for you to quickly test the program with. They should not be used for any real account that requires TOTP-based two-factor authentication. Usually, the issuer of a real account (such as an account on a website or an organization) would also issue a secret key or a secret QR code to you which you must use to generate TOTP values for the purpose of logging into that account.

With Base32 Key

  1. Enter this command:

    mintotp <<< ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS

    The output should be a 6-digit TOTP value.

  2. Add the following key to a TOTP-based authenticator app:

    ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS
    

    For example, if you have Google Authenticator on your mobile phone, open it, tap the button with plus sign, select "Enter a provided key", enter any account name and "Time-based" and enter the above key. Set the dropdown menu to "Time-based" and tap the "Add" button. A 6-digit TOTP value should appear for the new key.

  3. Run the command in step 1 again and verify that the TOTP value printed by the Python program matches the TOTP value that appears in the authenticator app.

With QR Code

  1. Install zbarimg to scan QR codes:

    # On macOS
    brew install zbar
    
    # On Debian, Ubuntu, etc.
    apt-get install zbar-tools
  2. Download and save the following QR code on your system:
    QR code for TOTP secret key
    The QR code above can also be found in this file: secret1.png.

  3. Enter this command to read the data in the QR code:

    zbarimg -q secret1.png

    The output should be:

    QR-Code:otpauth://totp/alice:bob?secret=ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS
    

    Note that the secret key in the URI is same as the secret key we used in the previous sections.

  4. Now enter this command to extract the secret key from the QR code and feed it to MinTOTP.

    zbarimg -q secret1.png | sed 's/.*secret=\([^&]*\).*/\1/' | mintotp
  5. Scan the QR code shown above in step 3 with a TOTP-based authenticator app. For example, if you have Google Authenticator on your mobile phone, open it, tap the button with plus sign, select "Scan a barcode", and scan the QR code shown above in step 3. A 6-digit TOTP value should appear for the new key.

  6. Run the command in step 4 again and verify that the TOTP value printed by MinTOTP matches the TOTP value that appears in the authenticator app.

Protect Key with GPG

The previous sections use an example key and QR code to show how this tool works. If you use this tool to generate TOTP values from a real secret key for a real account, you must encrypt your secret key to keep it safe. The next two subsections explain how to protect the key with GNU Privacy Guard (also known as GnuPG or GPG).

Install GPG

  1. Install GNU Privacy Guard:

    # On macOS
    brew install gnupg
    
    # On Debian, Ubuntu, etc.
    apt-get update
    apt-get install gnupg
  2. Enter the following command to ensure that GPG is installed.

    gpg --version

    If GPG is installed properly, the command above should print its version, license details, and cipher details.

Encrypt Base32 Key With GPG

The steps below show the usage of GPG to encrypt our example secret key. You would have to replace this example secret key with a real secret key that you want to use to generate TOTP values.

  1. Encrypt the secret key using GPG. First enter this command:

    gpg -c -o secret.gpg

    Then enter a strong passphrase when it prompts for it. Re-enter the passphase to confirm it. Then paste the following key as input:

    ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS
    

    Press enter to end the line. Press ctrl + d to end input. The encrypted secret key would be saved in a file named secret.gpg.

  2. Generate TOTP value from the encrypted key:

    gpg -q -o - secret.gpg | mintotp
  3. You can also generate TOTP value and copy it to system clipboard:

    # On macOS
    gpg -q -o - secret.gpg | mintotp | tr -d '\n' | pbcopy
    
    # On Linux
    gpg -q -o - secret.gpg | mintotp | tr -d '\n' | xclip

    Now you can easily paste the TOTP value to any login form that requires it. On Linux, of course, you need to have xclip installed to use it. On Debian, Ubuntu, etc. it can be installed with the apt-get install xclip command. To paste the value copied into the clipboard by xclip, middle-click on mouse.

  4. In case you want to see the TOTP value on the terminal while it is also copied to the system clipboard, use one of these commands:

    # On macOS
    gpg -q -o - secret.gpg | mintotp | tee /dev/stderr | tr -d '\n' | pbcopy
    
    # On Linux
    gpg -q -o - secret.gpg | mintotp | tee /dev/stderr | tr -d '\n' | xclip

Encrypt QR Code With GPG

The steps below show the usage of GPG to encrypt our example QR code. You would have to replace the example QR code with a real QR code that you want to use to generate TOTP values.

  1. Encrypt the QR code using GPG. First enter this command:

    gpg -c secret1.png

    Then enter a strong passphrase when it prompts for it. Re-enter the passphase to confirm it. The encrypted QR code would be saved in a file named secret1.png.gpg.

  2. Delete the unencrypted QR code file securely:

    # On macOS
    rm -P secret1.png
    
    # On Linux
    shred -u secret1.png
  3. Generate TOTP value from the encrypted QR code file:

    zbarimg -q <(gpg -q -o - secret1.png.gpg) | sed 's/.*secret=\([^&]*\).*/\1/' | mintotp
  4. You can also generate the TOTP value and copy it to system clipboard:

    # On macOS
    zbarimg -q <(gpg -q -o - secret1.png.gpg) | sed 's/.*secret=\([^&]*\).*/\1/' | mintotp | tr -d '\n' | pbcopy
    
    # On Linux
    zbarimg -q <(gpg -q -o - secret1.png.gpg) | sed 's/.*secret=\([^&]*\).*/\1/' | mintotp | tr -d '\n' | xclip
  5. In case you want to see the TOTP value on the terminal while it is also copied to the system clipboard, use one of these commands:

    # On macOS
    zbarimg -q <(gpg -q -o - secret1.png.gpg) | sed 's/.*secret=\([^&]*\).*/\1/' | mintotp | tee /dev/stderr | tr -d '\n' | pbcopy
    
    # On Linux
    zbarimg -q <(gpg -q -o - secret1.png.gpg) | sed 's/.*secret=\([^&]*\).*/\1/' | mintotp | tee /dev/stderr | tr -d '\n' | xclip

Usage

Multiple Keys

This tool accepts one or more Base32 secret keys as standard input. Each key must occur in its own line.

  1. Generate multiple TOTP values, one for each of multiple Base32 keys:

    mintotp <<eof
    ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS
    PW4YAYYZVDE5RK2AOLKUATNZIKAFQLZO
    eof
  2. Generate TOTP values for multiple keys in multiple QR codes:

    zbarimg -q *.png | sed 's/.*secret=\([^&]*\).*/\1/' | mintotp

Command Line Arguments

In order to keep this tool as minimal as possible, it does not come with any command line options. In fact, it does not even have the --help option. It does support a few command line arguments though. Since there is no help output from the tool, this section describes the command line arguments for this tool.

Here is a synopsis of the command line arguments supported by this tool:

mintotp [TIME_STEP [DIGITS [DIGEST]]]

Here is a description of each argument:

  • TIME_STEP

    TOTP time-step duration (in seconds) during which a TOTP value is valid. A new TOTP value is generated after time-step duration elapses. (Default: 30)

  • DIGITS

    Number of digits in TOTP value. (Default: 6)

  • DIGEST

    Cryptographic hash algorithm to use while generating TOTP value. (Default: sha1)

    Possible values are sha1, sha224, sha256, sha384, and sha512.

Here are some usage examples of these command line arguments:

  1. Generate TOTP value with a time-step size of 60 seconds:

    mintotp 60 <<< ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS
  2. Generate 8-digit TOTP value:

    mintotp 60 8 <<< ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS
  3. Use SHA-256 hash algorithm to generate TOTP value:

    mintotp 60 6 sha256 <<< ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS

The behaviour of the tool is undefined if it is used in any way other than what is described above. For example, although surplus command line arguments are ignored currently, this behaviour may change in future, so what should happen in case of surplus arguments is left undefined in this document.

Tradeoff

If you use this tool to generate TOTP values on a desktop/laptop device while logging into a website that requires TOTP-based two-factor (2FA) or multi-factor authentication (MFA) from the same device, you should be aware that doing so trades off some security for convenience.

2FA or MFA relies on the user presenting at least two pieces of evidence (factors) to an authentication system: something only the user knows and something only the user has.

If this tool is run to generate TOTP values on the same desktop/laptop device that you are using to log into a website, then you should consider that if your desktop/laptop device is compromised, then both authentication factors can be compromised. The attacker can steal the first authentication factor that only you should know (e.g., password) by running a key logger on the compromised device. The attacker can also steal the second authentication factor that only you should have (e.g., TOTP secret key) because it would be read by this tool on the same compromised device; if this tool can read the TOTP secret key on the compromised device, so can the attacker.

In other words, for higher security, it is good to generate TOTP values on a separate device. However, if the inconvenience of getting a separate device prevents you from using 2FA or MFA altogether, then you might find this tool helpful. It allows to trade off some security for convenience which is still more secure than not having 2FA or MFA at all. Whether trading some security for convenience is acceptable to you or not is something you need to decide for yourself.

Alternative: OATH Toolkit

There is an oathtool command available in OATH Toolkit that can also generate TOTP values from TOTP secret keys. This section presents some examples that show how oathtool can be used to do what MinTOTP does.

  1. Install OATH Toolkit:

    # On macOS
    brew install oath-toolkit
    
    # On Debian, Ubuntu, etc.
    apt-get update
    apt-get install oathtool
  2. Generate TOTP from a Base32 key:

    oathtool --totp -b - <<< ZYTYYE5FOAGW5ML7LRWUL4WTZLNJAMZS
  3. Generate TOTP from an encrypted Base32 key:

    gpg -q -o - secret.gpg | oathtool --totp -b -

    Section Encrypt Base32 Key With GPG explains how to create the encrypted key (secret.gpg) with GPG.

  4. Generate TOTP from a QR code:

    zbarimg -q secret1.png | sed 's/.*secret=\([^&]*\).*/\1/' | oathtool --totp -b -
  5. Generate TOTP from an encrypted QR code:

    zbarimg -q <(gpg -q -o - secret1.png.gpg) | sed 's/.*secret=\([^&]*\).*/\1/' | oathtool --totp -b -

    Section Encrypt QR Code With GPG explains how to create the encrypted QR code (secret1.png.gpg) with GPG.

  6. Generate TOTP value and copy it to system clipboard:

    # On macOS
    gpg -q -o - secret.gpg | oathtool --totp -b - | tr -d '\n' | pbcopy
    zbarimg -q <(gpg -q -o - secret1.png.gpg) | sed 's/.*secret=\([^&]*\).*/\1/' | oathtool --totp -b - | tr -d '\n' | pbcopy
    
    # On Linux
    gpg -q -o - secret.gpg | oathtool --totp -b - | tr -d '\n' | xclip
    zbarimg -q <(gpg -q -o - secret1.png.gpg) | sed 's/.*secret=\([^&]*\).*/\1/' | oathtool --totp -b - | tr -d '\n' | xclip

Resources

Here is a list of useful links about this project:

License

This is free and open source software. You can use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of it, under the terms of the MIT License. See LICENSE.md for details.

This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND, express or implied. See LICENSE.md for details.

Support

To report bugs, suggest improvements, or ask questions, please create a new issue at http://github.com/susam/mintotp/issues.

Thanks

Thanks to Prateek Nischal for getting me involved with TOTP. I referred to his TOTP implementation at prateeknischal/qry/util/totp.py while writing my own.

More Repositories

1

texme

Self-rendering Markdown + LaTeX documents
JavaScript
2,276
star
2

uncap

Map Caps Lock to Escape or any key to any key
C
1,048
star
3

gitpr

Quick reference guide on fork and pull request workflow
Makefile
957
star
4

spcss

A simple, minimal, classless stylesheet for simple HTML pages
CSS
939
star
5

emfy

A dark and sleek Emacs setup for general purpose editing and programming
Emacs Lisp
932
star
6

muboard

Self-rendering and distributable mathematics chalkboards
JavaScript
560
star
7

emacs4cl

A tiny DIY kit to set up vanilla Emacs for Common Lisp programming
Emacs Lisp
368
star
8

tucl

The first-ever paper on the Unix shell written by Ken Thompson in 1976 scanned, transcribed, and redistributed with permission
Makefile
355
star
9

mathb

Share mathematics on the web with LaTeX and Markdown
Common Lisp
308
star
10

cfrs

An extremely minimal drawing language consisting of only 6 simple commands: C, F, R, S, [, and ].
HTML
240
star
11

invaders

A 1980s-arcade-style game written using HTML5, Canvas, and Web Audio
HTML
166
star
12

hello

A 23-byte β€œhello, world” program assembled with DEBUG.EXE in MS-DOS
Assembly
156
star
13

devil

Emacs minor mode that intercepts and translates keystrokes to provide a modifier-free non-modal editing experience
Emacs Lisp
156
star
14

pov25

Ray tracing 25 scenes in 25 days with POV-Ray
POV-Ray SDL
154
star
15

fxyt

Tiny, esoteric, stack-based, postfix, canvas colouring language with only 36 simple commands
HTML
114
star
16

pcface

Bitmap arrays for rendering CP437 glyphs using IBM PC OEM fonts
Python
86
star
17

lab

Random experiments, exploration, and learning
Shell
80
star
18

vimer

Declutter your desktop by opening files in existing instance of GVim/MacVim
Shell
70
star
19

myrgb

A simple RGB color guessing game
HTML
67
star
20

quickqwerty

Touch typing tutor that runs in your web browser
JavaScript
61
star
21

ice

Ice - WSGI on the rocks
Python
58
star
22

aes.vbs

AES-256-CBC Encrypt and Decrypt Functions in VBScript
VBScript
52
star
23

heart

Hearts drawn with Python Matplotlib and C
Python
43
star
24

timebox

A timer script for Windows/Linux/Unix/macOS to practice timeboxing (the time management technique)
Shell
43
star
25

may4

Celebrating Star Wars Day with some Forth code! May the Forth be with you!
Forth
38
star
26

susam.net

Source code of https://susam.net/
Common Lisp
35
star
27

inwee

Conveniently send text and commands from file or standard input to WeeChat with this wrapper around WeeChat's FIFO pipe
Shell
25
star
28

bfc

Brainfuck compiler and interpreter
C
17
star
29

reboot

A 5-byte reboot program assembled with DEBUG.EXE in MS-DOS
Assembly
16
star
30

nimb

NIMB IRC Matrix Bridge (NIMB) is a simple client tool that bridges IRC and Matrix channels and forwards messages from one to others
Python
16
star
31

dotfiles

Mostly ~/.* files to configure emacs, vim, sh, tmux, etc. on Debian, macOS, and Windows
Shell
13
star
32

userscripts

Very tiny userscripts to customise the apperance of Hacker News
JavaScript
10
star
33

mano-cpu

My bachelor's degree final year project: A CPLD implementation of a 16-bit microprocessor I learnt from Computer System Architecture by M. Morris Mano
VHDL
8
star
34

susam.github.io

Mirror of https://susam.net/ generated from https://github.com/susam/susam.net
HTML
6
star
35

talks

Talks and presentations
TeX
6
star
36

maze

Susam's Maze β€’ Main website: https://susam.in/maze/ β€’ Mirror: https://susam.github.io/maze/
Common Lisp
5
star
37

licenses

Reusable copies of open source licenses
HTML
3
star
38

blob

Binary files generated from or used by other repositories
HTML
3
star
39

sunaina-and-susam

Sunaina & Susam's wedding website
HTML
2
star
40

.github

1
star
41

emacskeys

Screencasts of Emacs keys and commands
HTML
1
star