Follow the algorithm
As implemented in Google Authenticator app, you’ll need:
- A shared secret key K, which is
base32
encoded - a specific time period since UNIX epoch T
I’ll use K = GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
and T = 1450235092
(which is Wed, 16 Dec 2015 11:04:52 CST) for example. In the Google Authenticator App, both K and T are passed to TOTP as hexadecimal values (or byte arrays).
Since K is a base32
encoded string, we’ll decode it to byte format. Password changes every 30 seconds, T should be divided by 30, tuncated to integer and converted to hex and padded to 16 hexadecimal digits.
K = 12345678901234567890 K_hex = 31 21 33 34 35 36 37 38 39 30 31 21 33 34 35 36 37 38 39 30 T = truncate(1450235092/30) = 48341169 T_hex = 00 00 00 00 02 E1 A0 B1
The core of TOTP is a hmac-sha1
function, say mac_value = hmac-sha1(auth_code, content)
. We use K_hex
as the auth_code
and T_hex
as the content
.
Byte: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 mac_value = 3d 43 cb 58 89 39 a1 a4 bf 76 b9 dc 27 79 81 70 f7 22 a5 0c
Now we need an offset offset
= last 4 bits of mac_value
Byte: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 mac_value = 3d 43 cb 58 89 39 a1 a4 bf 76 b9 dc 27 79 81 70 f7 22 a5 0c offset = 12 ^
And cut off 4 bytes from offset
number of bytes to get an intermediate value IV1
Byte: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 mac_value = 3d 43 cb 58 89 39 a1 a4 bf 76 b9 dc 27 79 81 70 f7 22 a5 0c IV1 = ** ** ** ** ** ** ** ** ** ** ** ** 27 79 81 70 ** ** ** **
In case that big endian and little endian would interfere with generation, mask IV1
with 0x7FFFFFFF
IV1 = 27 79 81 70 mask = 7F FF FF FF IV2 = 27 79 81 70
then pick last 6 digits of IV2
IV2 = 662274416 Token = ***274416
So at Wed, 16 Dec 2015 11:04:52 CST
, with the key GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
, you get the token 274416
.
Full implementation
see my GitHub repository cthbleachbit/totp-cth-cli
, a bash implementation of a TOTP password manager, I used openssl
for hmac
hashing and database encryption.
Reference
base32
RFC 4648- TOTP RFC 6238
hmac-sha1
see RFC 2104 and hmac on Wikipedia