Tiny Toy TOTP Generator in 18 Lines of Python
github.com
github.com
https://git.sr.ht/~sircmpwn/meta.sr.ht/tree/master/metasrht/...
- RFC 4226 (HOTP): Section 7.4 (Resynchronization of the Counter): https://tools.ietf.org/html/rfc4226#section-7.4
- RFC 6238 (TOTP): Section 6 (Resynchronization): https://tools.ietf.org/html/rfc6238#section-6
In case of HOTP, the client's counter could be ahead of the server's counter if a user requests multiple HOTPs from the client before the user presents the HOTP to the server. Therefore the server should look ahead according to a permissible look-ahead window to see if any succeeding HOTP value matches the HOTP presented by the user or client.
In case of TOTP, there is of course the problem of clock drifts. Therefore the server should look backward as well as forward by a few time steps to see if any TOTP backward or forward matches the TOTP presented by the user or client.
> Note that a prover may send the same OTP inside a given time-step window multiple times to a verifier. The verifier MUST NOT accept the second attempt of the OTP after the successful validation has been issued for the first OTP, which ensures one-time only use of an OTP.
The beauty of HOTP/TOTP for me is its simplicity. And being able to write a simple implementation makes it easy to test, debug and be sure that there are little or no holes.
import base64
import hmac
import struct
import sys
import timeI mean, you could make the same claim about one written in C because it doesn’t include stdio.h. So where do you draw the line? Assembly?
In Python, the stdlib is effectively part of the language, so as long as they're not installing third-party libs, I don't see how it can be misleading.
This was an interesting thing to look at to see get a quick idea of how TOTP works.
On the flip side, though, if this we had this hypothetical implementation:
import totp
if __name__ == '__main__':
totp.generate()
Well, we probably wouldn't be here discussing it. mac = hmac.new(secret_bytes, counter_bytes, digest).digest()
and everything else is just converting data to/from the proper format for this function.Otherwise you probably wouldn't say you're implementing TOTP, you'd say you're implementing HMAC.
mac = hmac.new(secret_bytes, counter_bytes, digest).digest()
offset = mac[-1] & 0x0f
truncated = struct.unpack('>L', mac[offset:offset+4])[0] & 0x7fffffff
return str(truncated)[-digits:].rjust(digits, '0')
In other words, HMAC your secret key with the time and pick digits using the output of the HMAC.I built a small cli utility to manage my TOTP logins easily using them as reference.
Before I looked it up, I thought it was somehow related to NTP.
It's a little bit like expanding "transport control protocol". I mean, sure, I guess it's marginally better than the acronym TCP? But really, that's not the clarification you want to provide to someone who doesn't know what TCP is.
Also, obviously, if you don't know what TOTP is, you might just not be the audience for the article. Remember that most people don't write their personal blogs (or, in this case, their Github personal projects) specifically for an audience of Hacker News people, even though HN sometimes makes it seem that way.
> Introduction: TOTP stands for Time-based One-Time Password. At the heart of the TOTP algorithm lies the HOTP algorithm. HOTP stands for HMAC-based One-Time Password.
There are still some 2FA benefits from this. The biggest one is for people who reuse passwords. Some websites might put you into a higher security tier if you have 2FA. Also if you have to choose between SMS vs this, this has some benefits, for example it can't be hijacked by social engineering your phone provider.
Also it says regular TOTP protects you from keyloggers. That's not fully true, because the keylogger could steal your TOTP code as you enter it into the website. If the keylogger is realtime, it could log into your account before you're able to. Or if you assume there's malware on your computer, it could steal your cookies, or perform whatever account actions it wants directly on your computer.
> If your desktop/laptop device is compromised, then both authentication factors would be compromised. The attacker can steal the first authentication factor that only you should know (e.g., password) by running a key logger on the compromised device. The attacker can also steal the second authentication factor that only you should have ...
What I mean here is that generating the TOTP on the same system that we would use to log into a website with 2FA defeats the purpose of 2FA because the attacker can steal the TOTP secret key in addition to stealing the password (keylogger being one way to steal the password).
My question is why are we worrying so much about theft of the secret key? There are easier things to steal and abuse (cookies, TOTP codes, website data). Those can be stolen even if the TOTP generator is a different device than the logging in device.
I personally prefer this golang library to generate my OTP codes https://github.com/pquerna/otp as it's much faster than running a python script.
My personal computer is the 'what I have' to do the second factor of authentication for most sites via a shell script, followed by xclip (linux) or pbcopy (mac). I prefer this over browser extensions using javascript to paste in your OTP code for you.
It's amusing to see people think 100KB+ is small, when the whole TOTP algorithm, including SHA1 (probably the biggest part), and the input/output conversion, likely needs only a few KB of code. With the exception of SHA1, everything else is doable in dozens of bytes.
(Oathtool also supports SHA2, since it may be used by other implementations, per the RFC. )