Understanding OATH HOTP authentications
This article explains how OATH HOTP authentications work by describing the principles and intuitions behind them, and breaking down the HOTP value generation steps. The reader can try the commands in a ludic way using FreeOTP and Python.
Principles and intuitions
HOTP (HMAC-based One-Time Password) algorithm is an OTP (One-Time Password) algorithm based on HMAC (Hash-based Message Authentication Code). HMAC algorithm was published in February 1997 (in RFC-2104) and HOTP algorithm was published in December 2005 (in RFC-4226).
HOTP is one OATH (Open Authentication) cornerstone. OATH is an industry-wide collaboration that aims to promote strong authentication by leveraging existing open standards. HOTP provides an authentication method by symmetrically generating (on user side and on server side) human-readable codes or values, each can be used only once.
HOTP algorithm key principles
This article does not intent to describe in deep the cryptography operations, algorithms used but gives some intuitions behind them.
HOTP values are calculated by the operation HOTP(K,C) = truncate(HMAC(K,C)) mod 10^d where
- K is the secret key which is kept secret
- C is the counter which is incremented at each authentication
- K and C are shared by the user (in a token/mobile) and by the server
- d is the HOTP value size
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
- HOTP value size: 6
- Secret key: ABCDEFGHIJKLMNOP (in base 32)
Prerequisites
Create the HOTP algorithm characteristics
hash_algorithm = "sha1"
block_size = 64
hotp_value_size = 6
Create the secret key
secret_key = 'ABCDEFGHIJKLMNOP'
HOTP value generation with FreeOTP
FreeOTP is a 2FA (Two-Factor Authentication) application published by RedHat. It implements open standard HOTP.
Register the secret key (manually or by scanning QR code)
Generate the HOTP values corresponding to counter 1 to 5
HOTP value generation with Python
The Python PyOTP library implements open standard HOTP. It can be used to generate registration QR codes, generate and validate HOTP values.
Generate the HOTP values corresponding to counter 1 to 5
! pip install pyotp
import pyotphotp = pyotp.HOTP(secret_key)
hotp_values = [(hotp.at(x)) for x in range(0,5)]
for i in range(len(hotp_values)):
print('HOTP value corresponding to C = ' + str(i + 1) + ' is '+ hotp_values[i])HOTP value corresponding to C = 1 is 827178
HOTP value corresponding to C = 2 is 317963
HOTP value corresponding to C = 3 is 625848
HOTP value corresponding to C = 4 is 281014
HOTP value corresponding to C = 5 is 709708
The HOTP values are the same as the ones generated with FreeOTP
Breaking down the HOTP 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 HOTP algorithm
def hotp(counter):
hash = hmac(base64.b32decode(secret_key), counter.to_bytes(8, byteorder = 'big'))
truncated = truncate(hash)
hotp_value = truncated % 10 ** hotp_value_size
return hotp_value
Generate the HOTP values corresponding to counter 1 to 5
hotp_values = [(hotp(x)) for x in range(0, 5)]
for i in range(len(hotp_values)):
print('HOTP value corresponding to C = ' + str(i + 1) + ' is '+ str(hotp_values[i]))HOTP value corresponding to C = 1 is 827178
HOTP value corresponding to C = 2 is 317963
HOTP value corresponding to C = 3 is 625848
HOTP value corresponding to C = 4 is 281014
HOTP value corresponding to C = 5 is 709708
The HOTP values are the same as the ones generated with FreeOTP and Python PyOTP
OATH HOTP vs OATH TOTP
The counter can also be based on the current time. In this case, the algorithm is called TOTP (Time-based One-Time Password), a HOTP (HMAC-based One-Time Password) algorithm extension. See my article dedicated to TOTP here: https://bntan.medium.com/understanding-oath-totp-authentications-ed95b002a6e3.
Links
- Corresponding Jupyter notebook: https://github.com/bntan/oath-authentication/blob/master/oath-hotp-authentication/oath-hotp-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
- FreeOTP: https://freeotp.github.io
- PyOTP: https://github.com/pyauth/pyotp