Well - I have put together my own version (to avoid licensing issues) that does "Google 2FA" now (the following sample source is using a test vector that I found in researching this so both the "key" and "tm" are hard-coded for the test):
string key( "MZXW633PN5XW6" );
string secret = base32::decode( key );
uint64_t tm = 43499885;
uint8_t challenge[ 8 ];
for( int i = 8; i--; tm >>= 8 )
challenge[ i ] = tm;
string message( ( const char* )&challenge[ 0 ], 8 );
uint8_t hash[ 20 ];
hmac_sha1( secret, message, hash );
int offset = hash[ 19 ] & 0xf;
unsigned int truncatedHash = 0;
for( int i = 0; i < 4; ++i )
{
truncatedHash <<= 8;
truncatedHash |= hash[ offset + i ];
}
truncatedHash &= 0x7fffffff;
truncatedHash %= 1000000;
string pin = to_string( truncatedHash );
while( pin.length( ) < 6 )
pin = '0' + pin;
It's much uglier than the 3 line algo using SHA256 but I guess I won't be lectured now for "rolling my own crypto" with it.
Also interestingly enough from what I've read Google Authenticator does *not* strictly adhere to the RFC (I think this might be due to the way it does base32 conversions as it doesn't deal with padding).