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
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.