Blog RSS Feed Patreon: ViGreyTech GitHub: ViGrey Twitter: @ViGreyTech LinkedIn: Vi Grey

Google Authenticator Made Me Upset Enough to Make My Own

Sep 22, 2022

It's been a while since I did any writing. Not sure how off the rails or boring or ranty this will turn out to be as of starting this. You might also notice this blog post formatting style got a bit more simplistic if you're reading this on a web browser. The reason is because I set up a gemini capsule and am posting this there (If you're reading this from a gemini client, hello!). I'll probably write about gemini shortly.

Okay, now to the actual post...

What is Google Authenticator?

Google Authenticator is a free application that generates one time passwords for logging into services. This is usually used for multi-factor authentication and can be used on many services such as GitHub, Facebook, Twitter, Google, Discord, and others to help add multi-factor authentication to accounts. Using an authenticator app like Google Authenticator provides the user with some more security in case an adversary happens to know the user's username and password, as the adversary would also need the one time password, to successfully log in.

What's the Issue?

Imagine you're taking a walk by the lake and oops... your phone fell into the lake. Good luck logging into your accounts now.

"But that can happen with two factor authentication in general and is not a Google Authenticator only problem" you might say. "Don't put all your eggs in one basket. Make backups so you don't have a single point of failure."

We are almost at my issue with the app...

The Google Authenticator app has a way to backup your codes before a situation like that happens. To do so at the time of me writing this, you tap on the 3 dots that means it's a settings icon, tap "Transfer accounts", end then "Export accounts". You might have to type in your phone's PIN or password or similar next. You can then choose which accounts you want to export and that, a large QR code is displayed on the phone.

NOW WE ARE AT MY ISSUE!!!

Google Authenticator ONLY exports to a QR code. That's not bad, because I can just take a screenshot of it and decode it... EXCEPT you cannot take a screenshot in the Android version of Google Authenticator. If you want to export your codes, Google expects you to have a second smart phone. The following is stated on the screen with the QR code:

Download the Google Authenticator app on your new device. Within the app, scan this QR code.

Getting another smart phone to use as a backup source of 2-factor authentication codes seems a bit unnecessarily expensive and overkill.

My solution to this problem?

"Yeah well.. I'm Gonna Go Build My Own Authenticator App"

The Bender Meme

The protocols Google Authenticator uses are called TOTP (Time-Based One-Time Password [RFC 6238]) and HOTP (HMAC-Based One-Time Password [RFC 4226]), which are both public. They ended up being much simpler protocols than I initially expected.

RFC 4226 (HTTP/S Link)
RFC 4226 (Gemini Link) [Gemini Protocol Link]

RFC 6238 (HTTP/S Link)
RFC 6238 (Gemini Link) [Gemini Protocol Link]

I'll start by explaining HOTP a bit, as TOTP is essentially the same thing except is based on time.

HOTP relies on 3 values. A secret, a counter, and auth code size. The code size can be 6, 7, or 8. The secret can be any data but is usually represented over an about 16-24 character long base32 encoded string. The counter is a strictly incrementing value that usually starts at 0 and is incremented every time a code is generated. It is incredibly important that the service and the authenticator app stay syncronized or the codes will not be valid. The auth code size is the size of the code on the authenticator app.

To create an HOTP code, an authentication process called HMAC is used with the SHA1 cryptographic hash function, which we will call HMAC_SHA1. The value to generate the code is created by doing HMAC_SHA1(secret, counter), which will provide us with a 20 byte value. The last 4 bits of the code are used to dermine an offset, which can only be 0-15. Take the offset and grab 4 bytes from the code starting at that offset. Take the lowest order 31 bits (a mask of 0x7FFFFFFF) and convert it into an unsigned integer. The completed HOTP one time authentication code is the n rightmost decimal digits of that unsigned integer, where n is the auth code size (prepend zeroes to the code if needed).

Sorry for how long that paragraph got. TOTP will be quicker for me to explain.

TOTP is almost exactly the same as HOTP. The only differences are that a hash algorithm can be specified (HMAC with SHA1, HMAC with SHA256, or HMAC with SHA512) and intead of a counter, a value created using the current time is used. That time based counter is created by taking the amount of seconds since a specific starting time (usually 00:00 UTC on January 1, 1970), dividing by a period value (usually 30 seconds), and rounding that value down (floor rounding). That time value is a 64 bit integer. All of the other steps are identical to HOTP, including grabbing the final 4 bits of the hash.

The QR Codes From the Websites

When you sign up for 2 factor authentication using an authentication app on a website, the website will usually show you a QR code. They usually show a base32 string as well that can be manually entered into the authenticator app. That string is the secret key for TOTP (or HOTP).

The QR code points to a URI with a specific scheme. Rather than http:// or gemini:// it is otpauth://. The URI format is the following:

otpauth://{TYPE}/{LABEL}?{PARAMETERS}

{TYPE} can have the value of "totp" or "hotp" (excluding the quotation marks)

{LABEL} can have one of the following forms:

{ISSUER}:{ACCOUNTNAME}
{ISSUER}%3a{ACCOUNTNAME}
{ISSUER}%3A{ACCOUNTNAME}
{ACCOUNTNAME}

{ISSUER} is the issuer of the OTP code. This is usually the company name, like "Facebook" or "Google" (excluding the quotation marks)

{ACCOUNTNAME} is the the name of the account of this OTP code. This is usually the account username, like "test@example.com" or "@CoolAccount123" (excluding the quotation marks).

{PARAMETERS} are ampersand separated queries as described in RFC 3986.

RFC 4226 (HTTP/S Link)
RFC 4226 (Gemini Link) [Gemini Protocol Link]

There are a few different type of specified parameters. They are "secret", "issuer", "digits", "counter", and "period". The "secret" parameter is required and contains the base32 encoded secret. The "issuer" parameter functions the same as {ISSUER} before, although some authenticator apps might ignore the issuer parameter here or the {ISSUER} in the {LABEL} and not the other. The "digits" parameter is the amount of digits for the authentication code output. The "counter" parameter is only for hotp and is the counter used for HOTP (this parameter is used to ensure syncronization). The "period" parameter is only used for totp and is the amount of seconds between authentication codes. And finally, the "algorithm" parameter is only used for totp and specifies the hashing algorithm used with HMAC.

An example fully formatted otpauth URI is as follows:

otpauth://totp/GitHub:CoolAccount123?secret=ABCDEFGHIJKLMNOPQRST&period=30&digits=8&algorithm=SHA512

My Authenticator App

With the knowledge of TOTP, HOTP, and otpauth URIs, I ended up writing a program in Go that takes otpauth URI data and generates the required codes. It's incredibly barebones right now. Every second, the terminal is cleared using the ANSI escape codes \x1b[H\x1b[J and then the codes are printed onto the terminal. The amount of time remaining before the next code is also printed for each entry. The following image is the state it's in at this exact moment.

Basic Authenticator App In Action

I intend to make the authenticator available on GitHub under a BSD 2-Clause license after I make it look and feel better. It needs some sort of user interface and better way to get codes registered.

Google Authenticator is still a good application in my (probably misguided) option, but the difficulty to back up these codes can cause some real issues if someone loses their phone. I'll still end up using the Google Authenticator app, but if I need to set up 2-factor with an authenticator app, I will not set it up with Google Authenticator.