6

HOTP, the HMAC-based One-Time Password algorithm from RFC 4226, uses a "dynamic truncation" function to turn the 20 byte HMAC-SHA-1 value into a 31 bit string. The dynamic truncation (from Section 5.3) works like this (and is probably useless):

 DT(String) // String = String[0]...String[19]
 Let OffsetBits be the low-order 4 bits of String[19]
 Offset = StToNum(OffsetBits) // 0 <= OffSet <= 15
 Let P = String[OffSet]...String[OffSet+3]
 Return the Last 31 bits of P

TOTP (RFC 6238) allows using SHA-256 and SHA-512 as the HMAC hash in HOTP, but doesn't seem to define a new dynamic truncation function for use with them:

TOTP implementations MAY use HMAC-SHA-256 or HMAC-SHA-512 functions, based on SHA-256 or SHA-512 [SHA2] hash functions, instead of the HMAC-SHA-1 function that has been specified for the HOTP computation in [RFC4226].

Should I use low 4 bits of String[19] as offset, low 4 bits of String[length-1], or perhaps some other number of bits or a completely different truncation algorithm?

otus
  • 32,462
  • 5
  • 75
  • 167

1 Answers1

9

I managed to find it out by reproducing the test vectors.

TL;DR: The standard assumes that you use the low 4 bits of the last byte of the hash, regardless of its length. So replace 19 in the original DT definition with 31 for SHA-256 or 63 for SHA-512 and you are good to go.


Finding this out wasn't completely straightforward, as the standard only has a single test secret listed:

The test token shared secret uses the ASCII string value "12345678901234567890". With Time Step X = 30, and the Unix epoch as the initial value to count time steps, where T0 = 0, the TOTP algorithm will display the following values for specified modes and timestamps.

If fact, to reproduce the test vector values for SHA-256 and SHA-512 you have to extend that secret to 32/64 characters. I.e. use the ASCII strings "12345678901234567890123456789012" and "1234567890123456789012345678901234567890123456789012345678901234" instead.

otus
  • 32,462
  • 5
  • 75
  • 167