Understanding OATH HOTP authentications

Bun-Ny TAN
5 min readNov 8, 2020

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 pyotp
hotp = 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.

--

--