1
2
TOTP = HOTP(K, T)
HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))
  • T:T = (Current Unix time - T0) / X, T0 = 0,X = 30
  • K:
  • HOTP:

Time-based one-time password (TOTP) algorithm (RFC 6238) implies that an one-time password (OTP) is a product of two parameters encrypted together. These are a common value, which is a shared secret key; and a variable, in this case – the running time. These parameters are encrypted with a hash function.

Here’s a TOTP algorithm example to illustrate:

  1. A user wants to log into a TOTP 2FA protected application or website. For the OTP authentication to run, the user and the TOTP server need to initially share a static parameter (a secret key).
  2. When the client logs into the protected website, they have to confirm they possess the secret key. So their TOTP token merges the secret key and the current timestep and generates a HASH value by running a predetermined HASH function. This value essentially is the OTP code the user sees on the token.
  3. Since the secret key, the HASH function, and the timestep are the same for both parties, the server makes the same computation as the user’s OTP generator.
  4. The user enters the OTP and if it is identical to the server’s value, the access is granted. If the results of the calculations aren’t identical, the access is, naturally, denied.
TOTP-algorithm-explained.png

The secret key is a string of random characters, usually 16–32 characters long. “Sharing” the key usually implies scanning a QR code that shows the secret key generated by the server with the client’s TOTP app. Alternatively, the key is already programmed in their TOTP device. The timestep is calculated using UNIX time, which starts on January 1, 1970, UTC. The timesteps are to be 30 or 60 seconds, so the time value used for TOTP is the number of seconds run since 00:00 January 1, 1970, divided by 30, or 60.

The mentioned HASH function is a cryptographic mathematic function that simply changes one value into another and usually shortens the result to 6-8 symbols. This result is what we called a HASH value above.

All of this is specified in TOTP RFC.

TOTP synchronization problem:

An unavoidable problem with all hardware tokens is that their clocks can fall out of sync with the server. According to RFC 6238 (the TOTP standard), the possibility of time drift has to be taken into account on the server side.

1
2
3
4
5
//当前步数,30秒为一步
var userId = xx;
var step = (appCurrTimestamp - createTimestamp)/(30*1000);
//生成一个六位密码(这里生成密码的方法大家自己定义,保证安全性就行,核心逻辑是上一步计算步数,保证相同步数生成的密码相同即可)
var tempPass = substr(sha256(userId + secret + step),6);
1
2
3
4
5
6
7
//当前用户ID
var currUserId = xx;
//根据当前用户ID查询用户秘钥
var currUserSecret = querySecretByUserId(currUserId);
var step = (serverCurrTimestamp - createTimestamp)/(30*1000);
//生成密码(这里生成密码的方法大家自己定义,保证安全性就行,核心逻辑是上一步计算步数,保证相同步数生成的密码相同)
var serverTempPass =  substr(sha256(currUserId + currUserSecret + step),6);

边界情况如何处理?

服务器和手机的时间可能存在时间差(还有网络延迟造成的时间差),为了弥补时间差造成的步数不一致的问题,一般会向前和向后多算一步,只要这三步有一步是符合条件的,则符合条件。

I synced Google Authenticator and oathtool:

1
2
3
secret=`echo 1234567812345678 | base32`
oathtool -v --totp -b $secret
qrencode -t ANSI "otpauth://totp/test?secret="${secret}""

Reference

RFC 6238 TOTP

TOTP Algorithm Explained

oath-toolkit

TOTP library for Go

Google Authenticator OpenSource

GoogleAuth

TOTP 介绍及基于C#的简单实现

TOTP 和 Google 身份验证器

Initiative for Open Authentication

pam_oath

[a2fa](Annoying two-factor authentication)

2FA somewhat of an overkill

https://www.reddit.com/r/github/comments/164zdom/is_there_a_way_to_set_up_twofactor_authentication/

Or maybe I will just forget to do it and then get locked out forever.