Search code examples
haskellgoogle-authenticatortotp

Interacting with Google Authenticator Using Cryptonite


I am trying to generate TOTPs such that I can make use of the Google Authenticator app. I am using the cryptonite library which provides TOTP functionality.

From my understanding, Google Authenticator requires the key in Base32. It also requires the = padding removed. Cryptonite provides an unpad function but I don't really understand how it works and as such can't figure out what the first argument should be, everything I've tried makes the function return Nothing.

For testing purposes I chose a key that would have no padding however, the codes generated by my program do not match those generated by Google Authenticator.

The Base32 key produced by the code is the following:

GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ

Entering this into Google Authenticator does not produce the same results as my code produces.

Here is my code:

{-# LANGUAGE OverloadedStrings #-}

module TOTP
    ( genTOTP
    ) where

import qualified Basement.Imports        as Base (String)
import           Crypto.Data.Padding
import           Crypto.OTP              (defaultTOTPParams, totp)
import           Data.ByteArray
import           Data.ByteArray.Encoding
import           Data.Time.Clock.POSIX

genTOTP :: IO ()
genTOTP = do
    time <- getPOSIXTime
    let secret = "12345678901234567890" :: Base.String
        keyBytes = convertToBase Base32 secret :: Bytes
        key = keyBytes -- unpad (PKCS7 6) keyBytes -- I don't know what the first argument to unpad should be
        code = totp defaultTOTPParams key (round time)
    print code

In summary, I am unsure about how to unpad the = bytes from the key. Secondly, when testing with a key that had no padding, I still don't get the correct result.

EDIT: If it helps anyone, I did manage to find someone's implementation of Google Authenticator in Haskell without using cryptonite however, I still wasn't able to figure out why my code doesn't work. Google Authenticator in Haskell


Solution

  • I found the answer to my question in the following question relating to an implementation in Python, here.

    I was correct to convert the secret to base32 before typing it into Google Authenticator, but I didn't know that Google Authenticator converted it back from base32 to the regular secret before generating the code.

    If I simply call totp on secret having entered keyBytes to Google Authenticator, I get the matching codes.