Understanding OATH TOTP authentications
This article explains how OATH TOTP authentications work by describing the principles and intuitions behind them, and breaking down the TOTP value generation steps. The reader can try the commands in a ludic way using Google Authenticator and Python.
Principles and intuitions
TOTP (Time-based One-Time Password) algorithm is a HOTP (HMAC-based One-Time Password) algorithm extension. See my article dedicated to HOTP here: https://bntan.medium.com/understanding-oath-hotp-authentications-42dc6012c41d.
Like HOTP, TOTP is an OTP (One-Time Password) algorithm based on HMAC (Hash-based Message Authentication Code) but takes the current time as the counter. HMAC algorithm was published in February 1997 (in RFC-2104), HOTP algorithm was published in December 2005 (in RFC-4226), and TOTP algorithm was published in May 2011 (in RFC-6238).
TOTP is one OATH (Open Authentication) cornerstone. OATH is an industry-wide collaboration that aims to promote strong authentication by leveraging existing open standards. TOTP provides an authentication method by symmetrically generating (on user side and on server side) human-readable codes or values, each, based on the current time, can be used only once.
TOTP algorithm key principles
This article does not intent to describe in deep the cryptography operations, algorithms used but gives some intuitions behind them.
TOTP values are calculated by the operation TOTP (K) = HOTP(K,C) = truncate(HMAC(K,C)) mod 10^d where
- K is the secret key which is kept secret and shared by the user (in a token/mobile) and by the server
- C is the counter which is based on the current time (see below)
- d is the TOTP value size
Current time counter
The current time counter is calculated by the operation C = (T - To) / Tx where
- T is the current time (e.g. unix time)
- To is the epoch (e.g. unix epoch is 0)
- Tx is one time duration (time duration between 2 TOTP)
HMAC algorithm
where
- K is the secret key
- C is the counter
- H is a hash algorithm
- ipad is the block-sized inner padding which corresponds to repeated value 0x36
- opad is the block-sized outer padding which corresponds to repeated value 0x5c
- || denotes concatenation
- + denotes bitwise exclusive or (XOR)
Truncate operation
The truncate operation consists in
- Taking the HMAC low-order 4 bits
- Using them as a byte index i
- Selecting 4 bytes starting at byte index i
- truncate(HMAC) = HMAC[i:i+4]
Here is a truncate operation example
-------------------------------------------------------------
| HMAC Byte Number |
-------------------------------------------------------------
|00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|
-------------------------------------------------------------
| HMAC Byte Value |
-------------------------------------------------------------
|1f|86|98|69|0e|02|ca|16|61|85|50|ef|7f|19|da|8e|94|5b|55|5a|
-------------------------------***********-----------------+-
- HMAC low-order 4 bits is 0xa (see + underlining)
- The byte index i is 10 = 0xa
- The selected 4 bytes starting at index 10 are 0x50ef7f19 (see * underlining). This is the truncated HMAC
Let’s now generate HOTP values step by step in practice using:
- Hash algorithm: SHA-1
- Block size: 64 bytes
- TOTP value size: 6
- One time duration: 30 seconds
- Secret key: ABCDEFGHIJKLMNOP (in base 32)
Prerequisites
Create the TOTP algorithm characteristics
hash_algorithm = "sha1"
block_size = 64
totp_value_size = 6
duration = 30
Create the secret key
secret_key = 'ABCDEFGHIJKLMNOP'
TOTP value generation with Google Authenticator
Google Authenticator is a 2FA (Two-Factor Authentication) application published by Google. It implements open standard TOTP.
Register the secret key (manually or by scanning QR code)
Generate the TOTP values during 2 minutes every 30 seconds
TOTP value generation with Python
The Python PyOTP library implements open standard TOTP. It can be used to generate registration QR codes, generate and validate TOTP values.
Generate the TOTP values during 2 minutes every 30 seconds
! pip install pyotp
import pyotp
import timetotp = pyotp.TOTP(secret_key)
for i in (range(4)):
totp_value = totp.now()
print('TOTP value corresponding to time ' + str(int(time.time())) + ' is ' + str(totp_value))
if (i < 3):
print(' Waiting '+ str(duration) + ' seconds ', end = '')
for j in range (duration):
print('.', end = '')
time.sleep(1)
print()TOTP value corresponding to time 1604931390 is 389698
Waiting 30 seconds ..............................
TOTP value corresponding to time 1604931420 is 505916
Waiting 30 seconds ..............................
TOTP value corresponding to time 1604931450 is 262714
Waiting 30 seconds ..............................
TOTP value corresponding to time 1604931480 is 092212
The TOTP values are the same as the ones generated with Google Authenticator
Breaking down the TOTP value generation steps
import base64
import hashlib
Implement HMAC algorithm
def hmac(secret_key, counter):
i_pad = bytes((x ^ 0x36) for x in range(256))
o_pad = bytes((x ^ 0x5C) for x in range(256))
key_pad = secret_key.ljust(block_size, b'\0')
i_key_pad = key_pad.translate(i_pad)
o_key_pad = key_pad.translate(o_pad)
hash_sum_1 = hashlib.new(hash_algorithm, i_key_pad + counter).digest()
hash_sum_2 = hashlib.new(hash_algorithm, o_key_pad + hash_sum_1).digest()
hash = hash_sum_2
return hash
Implement truncation
def truncate(hash):
i = hash[-1] % 16
truncated = int.from_bytes(hash[i:i + 4], byteorder = 'big', signed = False) % 2 ** 31
return truncated
Implement TOTP algorithm
def totp():
hash = hmac(base64.b32decode(secret_key), int(time.time()/duration).to_bytes(8, byteorder = 'big'))
truncated = truncate(hash)
totp_value = truncated % 10 ** totp_value_size
return totp_value
Generate the TOTP values during 2 minutes every 30 seconds
for i in (range(4)):
totp_value = totp()
print('TOTP value corresponding to time ' + str(int(time.time())) + ' is ' + str(totp_value))
if (i < 3):
print(' Waiting '+ str(duration) + ' seconds ', end = '')
for j in range (duration):
print('.', end = '')
time.sleep(1)
print()
TOTP value corresponding to time 1604931390 is 389698
Waiting 30 seconds ..............................
TOTP value corresponding to time 1604931420 is 505916
Waiting 30 seconds ..............................
TOTP value corresponding to time 1604931450 is 262714
Waiting 30 seconds ..............................
TOTP value corresponding to time 1604931480 is 092212
The TOTP values are the same as the ones generated with Google Authenticator and Python PyOTP
Links
- Corresponding Jupyter notebook: https://github.com/bntan/oath-authentication/blob/master/oath-totp-authentication/oath-totp-authentication.ipynb
- HMAC: https://en.wikipedia.org/wiki/HMAC
- HMAC (RFC-2104): https://tools.ietf.org/html/rfc2104
- HOTP: https://en.wikipedia.org/wiki/HMAC-based_One-time_Password_algorithm
- HOTP (RFC-4226): https://tools.ietf.org/html/rfc4226
- TOTP: https://en.wikipedia.org/wiki/Time-based_One-time_Password_algorithm
- TOTP (RFC-6238): https://tools.ietf.org/html/rfc6238
- OATH: https://openauthentication.org
- Google Authenticator: https://en.wikipedia.org/wiki/Google_Authenticator
- PyOTP: https://github.com/pyauth/pyotp