lucadentella / TOTP-Arduino

155 stars 33 forks source link

TOTP: Secret confusion. Can't reproduce codes from App #20

Closed fpdragon closed 2 years ago

fpdragon commented 2 years ago

Hello,

I am trying to integrate your library and have found some strange behavior. Most likely I am using it somehow wrong but currently I don’t see the mistake.

The usage seems to be straight forward. You have to have the current UNIX Epoch time stamp and the “Secret” of the key you want to generate, in form of a char array. This, put together should generate the 6 digit number code as known from Google Authenticator or MS Authenticator Apps.

I finished my implementation and added a very easy secret test: “0123” -> {0x30, 0x31, 0x32, 0x33} I set up the Authenticator app with “0123”, I synced the time on my target hardware and… perfect. I get the same codes as the Google Authenticator App shows. Cool.

Then I started to experiment and use a real secret that is used for a google account login. I won’t post it here but I just can say that it is much longer, 32 digits long, and contains numbers and lower case letters coded as ASCII characters. I tried to use it exactly the same way as before by putting them to a char array. The result is that I get different numbers than on my Google Authenticator App. Bad. Then I found out that my TOTP secret code for my BitMEX account is completely different. It contains numbers and UPPER case letters and is 16 digits long in total. Testing this secret code also has not resulted in the generation of the correct OTPs.

So now I am confused. 1.) It seems that secret codes can have diffent codings and lengths. Additionally I have noticed on http://www.lucadentella.it/OTP/ that only secrets with a length of 20 characters max are accepted. So is there a limitation and why is my google code longer? Do I have to convert it first? 2.) All the 2FA apps are accepting these different secrets flawlessly. Is it that these apps detect the secret format and convert them before usage? Or maybe I am wrong and these different secret formats are just format prefered by the issuers and all should work fine and the same way? 3.) What’s about Base32? I thought maybe the secrets have to be decoded with Base32 before I can use them directly as an char array? Had no luck. 4.) Are there any library limitations or something else to keep in mind? Maybe the library can just handle secrets with a special length? 5.) Maybe there is a bug in the library? Anyone knows?

Hope you can help. All the best!

fpdragon commented 2 years ago

I figured it out.

Here is what I have learned:

The secret itself is also called "hmacKey" and has to be configured while initializing the TOTP module. It must have to have a binary byte array style and the data is completely unformatted. One could discuss if it is byte or character, unsigned or signed, however for me this is clearly a unsigned byte array data since the values can be from 0-255 and have no useful ASCII representation.

In the internet, QR codes are commonly used to configure a secret. This QR code contains a “pseudo URL” where just one parameter is the secret itself. Since an URL can’t hold all 0-255 character/byte values, the values have to be encoded with Base32. The encoded output should technically contain characters 0-7, A-Z (upper case) and ‘=’ only. Seems that the internet does not take it so serious with this and in reality Base32 encoded secrets are also served with a-z (lower case) and spaces ‘ ‘ but this is clearly not part of the legal Base32 character base (I guess). This is why we have many different formats for encoded secrets.

So, what to do, when you have a encoded secret and you want to implement a HW TOTP with Arduino? 1.) Remove all spaces from the encoded secret 2.) Convert all a-z characters to upper case (A-Z) 3.) DECODE the secret regarding Base32. Be aware of that the result is NOT an ASCII string. It is binary data that is not viewable in a normal text editor. I recommend using an unsigned byte array. Typically, it is 10 or 20 bytes long. 4.) Initialize the TOTP Arduino module with the decoded secret array. 5.) Sync up the time… 6.) Generate the 6-digit number codes.

It seems that the commonly used TOTPs are all working with SHA1. At least for me, every secret I have tested seems to work. Hope this helps someone.

lucadentella commented 2 years ago

Hi, thanks for your comment. I'm going to prepare an FAQ: there are a lot of precious information in this and in other issues!